From cc466d5e41a06e09f09959a3fabe9124680a55c9 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 15:14:30 -0300 Subject: [PATCH 01/10] Add ES8 syntax --- .eslintrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.eslintrc b/.eslintrc index 4739f5f..6144af8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,5 +12,8 @@ "env": { "node": true }, + "parserOptions": { + "ecmaVersion": 8 + }, "extends": "eslint:recommended" } From feee44fac4bb48de236b2f28f82c2f7c58257d39 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 15:17:30 -0300 Subject: [PATCH 02/10] refactor: Remove async package; Rewrite syntax --- build/build.js | 103 +++++++++++++++++++++++-------------------------- package.json | 14 +++---- 2 files changed, 54 insertions(+), 63 deletions(-) diff --git a/build/build.js b/build/build.js index 806fb10..6c5749b 100644 --- a/build/build.js +++ b/build/build.js @@ -1,79 +1,74 @@ -var async = require('async'); -var fs = require('fs'); -var path = require('path'); +const fs = require('fs'); +const path = require('path'); // File paths -var EMOJI_PATH = path.resolve(__dirname, 'Emoji_Sentiment_Data_v1.0.csv'); -var RESULT_PATH = path.resolve(__dirname, 'emoji.json'); +const EMOJI_PATH = path.resolve(__dirname, 'Emoji_Sentiment_Data_v1.0.csv'); +const RESULT_PATH = path.resolve(__dirname, 'emoji.json'); /** * Read emoji data from original format (CSV). - * @param {object} hash Result hash - * @param {Function} callback Callback - * @return {void} + * @param {object} hash Result hash. + * @return {object} hash Result hash. */ -function processEmoji(hash, callback) { +const processEmoji = async hash => { // Read file - fs.readFile(EMOJI_PATH, 'utf8', function (err, data) { - if (err) return callback(err); - + try { + const data = await fs.readFileSync(EMOJI_PATH, 'utf8'); // Split data by new line - data = data.split(/\n/); - + const lines = data.split(/\n/); // Iterate over dataset and add to hash - for (var i in data) { - var line = data[i].split(','); - + for (var i in lines) { + const line = lines[i].split(','); // Validate line - if (i == 0) continue; // Label - if (line.length !== 9) continue; // Invalid + if (i === '0' || i === 0 || line.length !== 9) continue; + // ^ Label ^ Label ^ Invalid // Establish sentiment value - var emoji = String.fromCodePoint(line[1]); - var occurences = line[2]; - var negCount = line[4]; - var posCount = line[6]; - var score = (posCount / occurences) - (negCount / occurences); - var sentiment = Math.floor(5 * score); + const emoji = String.fromCodePoint(line[1]); + const occurences = line[2]; + const negCount = line[4]; + const posCount = line[6]; + const score = posCount / occurences - negCount / occurences; + const sentiment = Math.floor(5 * score); // Validate score - if (Number.isNaN(sentiment)) continue; - if (sentiment === 0) continue; + if (Number.isNaN(sentiment) || sentiment === 0) continue; // Add to hash hash[emoji] = sentiment; } - - callback(null, hash); - }); -} + return hash; + } catch (e) { + throw new Error(e); + } +}; /** * Write sentiment score hash to disk. * @param {object} hash Result hash - * @param {Function} callback Callback - * @return {void} + * @return {object} hash Result hash */ -function finish(hash, callback) { - var result = JSON.stringify(hash, null, 4); - fs.writeFile(RESULT_PATH, result, function (err) { - if (err) return callback(err); - callback(null, hash); - }); -} +const finish = async hash => { + const result = JSON.stringify(hash, null, 4); + try { + await fs.writeFileSync(RESULT_PATH, result); + return hash; + } catch (e) { + throw new Error(e); + } +}; // Execute build process -async.waterfall([ - function (cb) { - cb(null, {}); - }, - processEmoji, - finish -], function(err, result) { - if (err) throw new Error(err); - process.stderr.write( - 'Complete: ' + - Object.keys(result).length + - ' entries.\n' - ); -}); +const build = async () => { + try { + let hash = {}; + hash = await processEmoji(hash); + hash = await finish(hash); + process.stderr.write( + 'Complete: ' + Object.keys(hash).length + ' entries.\n' + ); + } catch (e) { + throw new Error(e); + } +}; +build(); diff --git a/package.json b/package.json index c10751d..fe5fa3c 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,7 @@ "type": "git", "url": "https://github.com/thisandagain/sentiment.git" }, - "keywords": [ - "sentiment", - "analysis", - "nlp", - "sentiment analysis" - ], + "keywords": ["sentiment", "analysis", "nlp", "sentiment analysis"], "main": "./lib/index.js", "scripts": { "build": "node ./build/build.js", @@ -23,12 +18,13 @@ "test:integration": "tap test/integration/*.js", "test:benchmark": "node ./test/benchmark/performance.js", "test:validate": "node ./test/benchmark/validate.js", - "test:coverage": "tap './test/{integration,unit}/*.js' --coverage --coverage-report=lcov", - "test": "npm run test:lint && npm run test:unit && npm run test:integration && npm run test:benchmark && npm run test:validate" + "test:coverage": + "tap './test/{integration,unit}/*.js' --coverage --coverage-report=lcov", + "test": + "npm run test:lint && npm run test:unit && npm run test:integration && npm run test:benchmark && npm run test:validate" }, "devDependencies": { "Sentimental": "1.0.1", - "async": "^2.1.5", "benchmark": "^2.1.0", "eslint": "^4.6.1", "mock-require": "^3.0.1", From e01301704b0e2c51d5d94280c28f49241fc050e5 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 16:02:15 -0300 Subject: [PATCH 03/10] refactor: Rewrite tests syntax --- test/benchmark/performance.js | 24 +++--- test/benchmark/validate.js | 30 ++++---- test/fixtures/fuzz.js | 34 ++++----- test/integration/add_lang.js | 10 +-- test/integration/async_inject.js | 16 ++-- test/integration/async_negative.js | 12 +-- .../async_negative_text_and_emoji.js | 12 +-- test/integration/async_positive.js | 12 +-- .../async_positive_text_and_emoji.js | 12 +-- test/integration/custom_lang.js | 14 ++-- test/integration/gh_12.js | 12 +-- test/integration/gh_13.js | 12 +-- test/integration/gh_85.js | 12 +-- test/integration/supported_lang.js | 16 ++-- test/integration/sync_corpus.js | 14 ++-- test/integration/sync_fuzz.js | 12 +-- test/integration/sync_inject.js | 16 ++-- test/integration/sync_negation.js | 12 +-- test/integration/sync_negative.js | 12 +-- .../sync_negative_text_and_emoji.js | 12 +-- test/integration/sync_positive.js | 12 +-- .../sync_positive_text_and_emoji.js | 12 +-- test/integration/sync_undefined.js | 12 +-- test/unit/language-processor.js | 33 +++------ test/unit/spec.js | 16 ++-- test/unit/tokenize.js | 74 ++++++++++++------- 26 files changed, 232 insertions(+), 233 deletions(-) diff --git a/test/benchmark/performance.js b/test/benchmark/performance.js index caa9499..71f1a02 100644 --- a/test/benchmark/performance.js +++ b/test/benchmark/performance.js @@ -8,36 +8,36 @@ /** * Dependencies */ -var Benchmark = require('benchmark'); -var suite = new Benchmark.Suite(); +const Benchmark = require('benchmark'); +const suite = new Benchmark.Suite(); -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); -var sentimental = require('Sentimental'); +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); +const sentimental = require('Sentimental'); /** * Test data */ -var stringShort = 'This cat is totally awesome'; -var stringLong = require('../fixtures/corpus'); +const stringShort = 'This cat is totally awesome'; +const stringLong = require('../fixtures/corpus'); /** * Setup */ suite - .add('sentiment (Latest) - Short ', function () { + .add('sentiment (Latest) - Short', () => { sentiment.analyze(stringShort); }) - .add('sentiment (Latest) - Long ', function () { + .add('sentiment (Latest) - Long ', () => { sentiment.analyze(stringLong); }) - .add('Sentimental (1.0.1) - Short', function () { + .add('Sentimental (1.0.1) - Short', () => { sentimental.analyze(stringShort); }) - .add('Sentimental (1.0.1) - Long ', function () { + .add('Sentimental (1.0.1) - Long ', () => { sentimental.analyze(stringLong); }) - .on('cycle', function (event) { + .on('cycle', event => { process.stdout.write(String(event.target) + '\n'); }) .run({ diff --git a/test/benchmark/validate.js b/test/benchmark/validate.js index 7a7d0ff..793add2 100644 --- a/test/benchmark/validate.js +++ b/test/benchmark/validate.js @@ -1,26 +1,26 @@ -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var amazon = require('../fixtures/amazon.json'); -var imdb = require('../fixtures/imdb.json'); -var yelp = require('../fixtures/yelp.json'); +const amazon = require('../fixtures/amazon.json'); +const imdb = require('../fixtures/imdb.json'); +const yelp = require('../fixtures/yelp.json'); -function validate (set) { +function validate(set) { // Storage object - var obj = { + const obj = { pass: 0, fail: 0 }; // Iterate over each word/class pair in the dataset for (var i in set) { - var score = sentiment.analyze(set[i].text).comparative; + const score = sentiment.analyze(set[i].text).comparative; if (set[i].class === 0) { - if (score >= 0) obj.fail++; - if (score < 0) obj.pass++; + if (score >= 0) ++obj.fail; + if (score < 0) ++obj.pass; } else { - if (score >= 0) obj.pass++; - if (score < 0) obj.fail++; + if (score >= 0) ++obj.pass; + if (score < 0) ++obj.fail; } } @@ -28,6 +28,6 @@ function validate (set) { return obj.pass / (obj.pass + obj.fail); } -process.stdout.write('Amazon accuracy: ' + validate(amazon) + '\n'); -process.stdout.write('IMDB accuracy: ' + validate(imdb) + '\n'); -process.stdout.write('Yelp accuracy: ' + validate(yelp) + '\n'); +process.stdout.write(`Amazon accuracy: ${validate(amazon)}\n`); +process.stdout.write(` IMDB accuracy: ${validate(imdb)}\n`); +process.stdout.write(` Yelp accuracy: ${validate(yelp)}\n`); diff --git a/test/fixtures/fuzz.js b/test/fixtures/fuzz.js index 5d1e453..064b741 100644 --- a/test/fixtures/fuzz.js +++ b/test/fixtures/fuzz.js @@ -1,33 +1,25 @@ -function rand (limit) { - return Math.floor(Math.random() * limit); -} +const rand = limit => Math.floor(Math.random() * limit); -function createRandomWord (length) { - var consonants = 'bcdfghjklmnpqrstvwxyz!@#$%^&*()_+":;\'?><~`'; - var vowels = 'aeiou'; - var word = ''; - - // Split - consonants = consonants.split(''); - vowels = vowels.split(''); +const createRandomWord = length => { + const consonants = 'bcdfghjklmnpqrstvwxyz!@#$%^&*()_+":;\'?><~`'.split(''); + const vowels = 'aeiou'.split(''); + let word = ''; // Create word for (var i = 0; i < length / 2; i++) { - var randConsonant = consonants[rand(consonants.length)]; - var randVowel = vowels[rand(vowels.length)]; - - word += (i===0) ? randConsonant.toUpperCase() : randConsonant; - word += i*2 { + const words = []; for (var i = 0; i < length; i++) { words.push(createRandomWord(rand(20))); } - return words.join(' '); }; diff --git a/test/integration/add_lang.js b/test/integration/add_lang.js index b029c5c..96ccb59 100644 --- a/test/integration/add_lang.js +++ b/test/integration/add_lang.js @@ -1,9 +1,9 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -test('adding a language with no labels attribute should throw', function (t) { - t.throws(function () { +test('adding a language with no labels attribute should throw', t => { + t.throws(() => { sentiment.registerLanguage('xx', {}); }, new Error('language.labels must be defined!')); t.end(); diff --git a/test/integration/async_inject.js b/test/integration/async_inject.js index 59077b8..772f4cd 100644 --- a/test/integration/async_inject.js +++ b/test/integration/async_inject.js @@ -1,14 +1,14 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool'; -var options = { - extras: { 'cool': 100 } +const input = 'This is so cool'; +const options = { + extras: { cool: 100 } }; -sentiment.analyze(input, options, function (err, result) { - test('asynchronous inject', function (t) { +sentiment.analyze(input, options, (err, result) => { + test('asynchronous inject', t => { t.type(result, 'object'); t.equal(result.score, 100); t.equal(result.comparative, 25); diff --git a/test/integration/async_negative.js b/test/integration/async_negative.js index ac60a27..17a82bd 100644 --- a/test/integration/async_negative.js +++ b/test/integration/async_negative.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'Hey you worthless scumbag'; +const input = 'Hey you worthless scumbag'; -sentiment.analyze(input, function (err, result) { - test('asynchronous negative', function (t) { +sentiment.analyze(input, (err, result) => { + test('asynchronous negative', t => { t.type(result, 'object'); t.equal(result.score, -6); t.equal(result.comparative, -1.5); diff --git a/test/integration/async_negative_text_and_emoji.js b/test/integration/async_negative_text_and_emoji.js index dc5c9d1..eed9465 100644 --- a/test/integration/async_negative_text_and_emoji.js +++ b/test/integration/async_negative_text_and_emoji.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'Hey you worthless scumbag 😦'; +const input = 'Hey you worthless scumbag 😦'; -sentiment.analyze(input, function (err, result) { - test('asynchronous negative text and emoji', function (t) { +sentiment.analyze(input, (err, result) => { + test('asynchronous negative text and emoji', t => { t.type(result, 'object'); t.equal(result.score, -8); t.equal(result.comparative, -1.6); diff --git a/test/integration/async_positive.js b/test/integration/async_positive.js index afb2f0a..94cf751 100644 --- a/test/integration/async_positive.js +++ b/test/integration/async_positive.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool'; +const input = 'This is so cool'; -sentiment.analyze(input, function (err, result) { - test('asynchronous positive', function (t) { +sentiment.analyze(input, (err, result) => { + test('asynchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 1); t.equal(result.comparative, 0.25); diff --git a/test/integration/async_positive_text_and_emoji.js b/test/integration/async_positive_text_and_emoji.js index abf18d5..8b1d18b 100644 --- a/test/integration/async_positive_text_and_emoji.js +++ b/test/integration/async_positive_text_and_emoji.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool 😃'; +const input = 'This is so cool 😃'; -sentiment.analyze(input, function (err, result) { - test('asynchronous positive text and emoji', function (t) { +sentiment.analyze(input, (err, result) => { + test('asynchronous positive text and emoji', t => { t.type(result, 'object'); t.equal(result.score, 3); t.equal(result.comparative, 0.6); diff --git a/test/integration/custom_lang.js b/test/integration/custom_lang.js index c916790..3dc4598 100644 --- a/test/integration/custom_lang.js +++ b/test/integration/custom_lang.js @@ -1,15 +1,15 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool'; +const input = 'This is so cool'; sentiment.registerLanguage('xx', { - labels: { 'cool': 5 } + labels: { cool: 5 } }); -sentiment.analyze(input, { language: 'xx' }, function (err, result) { - test('asynchronous positive', function (t) { +sentiment.analyze(input, { language: 'xx' }, (err, result) => { + test('asynchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 5); t.equal(result.comparative, 1.25); diff --git a/test/integration/gh_12.js b/test/integration/gh_12.js index 1d8f01a..90f42fc 100644 --- a/test/integration/gh_12.js +++ b/test/integration/gh_12.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'self-deluded'; -var result = sentiment.analyze(input); +const input = 'self-deluded'; +const result = sentiment.analyze(input); -test('synchronous positive', function (t) { +test('synchronous positive', t => { t.type(result, 'object'); t.equal(result.score, -2); t.equal(result.comparative, -2); diff --git a/test/integration/gh_13.js b/test/integration/gh_13.js index 2903cc0..9e53cda 100644 --- a/test/integration/gh_13.js +++ b/test/integration/gh_13.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'constructor'; -var result = sentiment.analyze(input); +const input = 'constructor'; +const result = sentiment.analyze(input); -test('synchronous positive', function (t) { +test('synchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 0); t.equal(result.comparative, 0); diff --git a/test/integration/gh_85.js b/test/integration/gh_85.js index b3ea090..1dca164 100644 --- a/test/integration/gh_85.js +++ b/test/integration/gh_85.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'i\'ll be there soon'; -var result = sentiment.analyze(input); +const input = 'i\'ll be there soon'; +const result = sentiment.analyze(input); -test('synchronous positive', function (t) { +test('synchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 0); t.equal(result.comparative, 0); diff --git a/test/integration/supported_lang.js b/test/integration/supported_lang.js index 5db1679..493d57d 100644 --- a/test/integration/supported_lang.js +++ b/test/integration/supported_lang.js @@ -1,17 +1,17 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); -var mock = require('mock-require'); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); +const mock = require('mock-require'); +const input = 'This is so cool'; // Mock a supported language mock('../../languages/yy/index', { - labels: { 'cool': 20 } + labels: { cool: 20 } }); -var input = 'This is so cool'; -var result = sentiment.analyze(input, { language: 'yy' }); +const result = sentiment.analyze(input, { language: 'yy' }); -test('synchronous positive', function (t) { +test('synchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 20); t.equal(result.comparative, 5); diff --git a/test/integration/sync_corpus.js b/test/integration/sync_corpus.js index 72d45ea..b6f17ee 100644 --- a/test/integration/sync_corpus.js +++ b/test/integration/sync_corpus.js @@ -1,12 +1,12 @@ -var test = require('tap').test; -var corpus = require('../fixtures/corpus'); -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const corpus = require('../fixtures/corpus'); +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var dataset = corpus; -var result = sentiment.analyze(dataset); +const dataset = corpus; +const result = sentiment.analyze(dataset); -test('synchronous corpus', function (t) { +test('synchronous corpus', t => { t.type(result, 'object'); t.equal(result.score, -3); t.equal(result.tokens.length, 1416); diff --git a/test/integration/sync_fuzz.js b/test/integration/sync_fuzz.js index 9a94d53..5b1bca7 100644 --- a/test/integration/sync_fuzz.js +++ b/test/integration/sync_fuzz.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var fuzz = require('../fixtures/fuzz'); -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const fuzz = require('../fixtures/fuzz'); +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = fuzz(1000); +const input = fuzz(1000); -test('synchronous fuzz', function (t) { +test('synchronous fuzz', t => { t.type(sentiment.analyze(input), 'object'); t.end(); }); diff --git a/test/integration/sync_inject.js b/test/integration/sync_inject.js index c8dd90c..9f4fab8 100644 --- a/test/integration/sync_inject.js +++ b/test/integration/sync_inject.js @@ -1,15 +1,15 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool'; -var options = { - extras: { 'cool': 100 } +const input = 'This is so cool'; +const options = { + extras: { cool: 100 } }; -var result = sentiment.analyze(input, options); +const result = sentiment.analyze(input, options); -test('synchronous inject', function (t) { +test('synchronous inject', t => { t.type(result, 'object'); t.equal(result.score, 100); t.equal(result.comparative, 25); diff --git a/test/integration/sync_negation.js b/test/integration/sync_negation.js index 0635a9b..28674df 100644 --- a/test/integration/sync_negation.js +++ b/test/integration/sync_negation.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'I don\'t hate you'; -var result = sentiment.analyze(input); +const input = 'I don\'t hate you'; +const result = sentiment.analyze(input); -test('synchronous negation', function (t) { +test('synchronous negation', t => { t.type(result, 'object'); t.equal(result.score, 3); t.equal(result.comparative, 0.75); diff --git a/test/integration/sync_negative.js b/test/integration/sync_negative.js index cb6019a..7b153f2 100644 --- a/test/integration/sync_negative.js +++ b/test/integration/sync_negative.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'Hey you worthless scumbag'; -var result = sentiment.analyze(input); +const input = 'Hey you worthless scumbag'; +const result = sentiment.analyze(input); -test('synchronous negative', function (t) { +test('synchronous negative', t => { t.type(result, 'object'); t.equal(result.score, -6); t.equal(result.comparative, -1.5); diff --git a/test/integration/sync_negative_text_and_emoji.js b/test/integration/sync_negative_text_and_emoji.js index 4514f7f..5e3cdf8 100644 --- a/test/integration/sync_negative_text_and_emoji.js +++ b/test/integration/sync_negative_text_and_emoji.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'Hey you worthless scumbag 😦'; -var result = sentiment.analyze(input); +const input = 'Hey you worthless scumbag 😦'; +const result = sentiment.analyze(input); -test('synchronous negative with emoji', function (t) { +test('synchronous negative with emoji', t => { t.type(result, 'object'); t.equal(result.score, -8); t.equal(result.comparative, -1.6); diff --git a/test/integration/sync_positive.js b/test/integration/sync_positive.js index 35260a0..0b485ab 100644 --- a/test/integration/sync_positive.js +++ b/test/integration/sync_positive.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool'; -var result = sentiment.analyze(input); +const input = 'This is so cool'; +const result = sentiment.analyze(input); -test('synchronous positive', function (t) { +test('synchronous positive', t => { t.type(result, 'object'); t.equal(result.score, 1); t.equal(result.comparative, 0.25); diff --git a/test/integration/sync_positive_text_and_emoji.js b/test/integration/sync_positive_text_and_emoji.js index f9bf464..4e950df 100644 --- a/test/integration/sync_positive_text_and_emoji.js +++ b/test/integration/sync_positive_text_and_emoji.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = 'This is so cool 😃'; -var result = sentiment.analyze(input); +const input = 'This is so cool 😃'; +const result = sentiment.analyze(input); -test('synchronous positive with emoji', function (t) { +test('synchronous positive with emoji', t => { t.type(result, 'object'); t.equal(result.score, 3); t.equal(result.comparative, 0.6); diff --git a/test/integration/sync_undefined.js b/test/integration/sync_undefined.js index 33b91af..608013a 100644 --- a/test/integration/sync_undefined.js +++ b/test/integration/sync_undefined.js @@ -1,11 +1,11 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -var input = undefined; -var result = sentiment.analyze(input); +const input = undefined; +const result = sentiment.analyze(input); -test('synchronous undefined', function (t) { +test('synchronous undefined', t => { t.type(result, 'object'); t.equal(result.score, 0); t.equal(result.comparative, 0); diff --git a/test/unit/language-processor.js b/test/unit/language-processor.js index c546da7..a2901a1 100644 --- a/test/unit/language-processor.js +++ b/test/unit/language-processor.js @@ -1,7 +1,8 @@ -var test = require('tap').test; -var languageProcessor = require('../../lib/language-processor'); +const test = require('tap').test; +const languageProcessor = require('../../lib/language-processor'); +const englishLanguage = require('../../languages/en/index'); -test('spec', function (t) { +test('spec', t => { t.type(languageProcessor, 'object'); t.type(languageProcessor.getLanguage, 'function'); t.type(languageProcessor.getLabels, 'function'); @@ -9,29 +10,13 @@ test('spec', function (t) { t.end(); }); -test('getLanguage', function (t) { - - var englishLanguage = require('../../languages/en/index'); - - t.deepEqual( - languageProcessor.getLanguage(), - englishLanguage - ); - - t.deepEqual( - languageProcessor.getLanguage(null), - englishLanguage - ); - - t.deepEqual( - languageProcessor.getLanguage('en'), - englishLanguage - ); - - t.throws(function () { +test('getLanguage', t => { + t.deepEqual(languageProcessor.getLanguage(), englishLanguage); + t.deepEqual(languageProcessor.getLanguage(null), englishLanguage); + t.deepEqual(languageProcessor.getLanguage('en'), englishLanguage); + t.throws(() => { // Should throw with unknown language code languageProcessor.getLanguage('xx'); }); - t.end(); }); diff --git a/test/unit/spec.js b/test/unit/spec.js index 1d14a5c..7dd8154 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -1,17 +1,21 @@ -var test = require('tap').test; -var Sentiment = require('../../lib/index'); -var sentiment = new Sentiment(); +const test = require('tap').test; +const Sentiment = require('../../lib/index'); +const sentiment = new Sentiment(); -test('module', function (t) { +test('module', t => { t.type(Sentiment, 'function', 'module is a function'); t.end(); }); -test('interface', function (t) { +test('interface', t => { t.type(sentiment, 'object', 'instance is an object'); t.type(sentiment.analyze, 'function', 'sentiment.analyze is a function'); // eslint-disable-next-line max-len - t.type(sentiment.registerLanguage, 'function', 'sentiment.registerLanguage is a function'); + t.type( + sentiment.registerLanguage, + 'function', + 'sentiment.registerLanguage is a function' + ); t.type(sentiment.analyze('test'), 'object'); t.type(sentiment.analyze('test', { test: 10 }), 'object'); t.end(); diff --git a/test/unit/tokenize.js b/test/unit/tokenize.js index 4d8795f..a9f7bf6 100644 --- a/test/unit/tokenize.js +++ b/test/unit/tokenize.js @@ -1,48 +1,66 @@ -var test = require('tap').test; -var tokenize = require('../../lib/tokenize'); +const test = require('tap').test; +const tokenize = require('../../lib/tokenize'); -test('spec', function (t) { +test('spec', t => { t.type(tokenize, 'function'); t.type(tokenize('foo'), 'object'); t.equal(tokenize('foo bar').length, 2); - t.throws(function () { + t.throws(() => { tokenize(123); }); - t.throws(function () { + t.throws(() => { tokenize({}); }); - t.throws(function () { + t.throws(() => { tokenize([]); }); t.end(); }); -test('english', function (t) { - t.deepEqual( - tokenize('The cat went over the wall.'), - ['the', 'cat', 'went', 'over', 'the', 'wall'] - ); - t.deepEqual( - tokenize('That\'ll cause problems for the farmer\'s pigs'), - ['that\'ll', 'cause', 'problems', 'for', 'the', 'farmer\'s', 'pigs'] - ); +test('english', t => { + t.deepEqual(tokenize('The cat went over the wall.'), [ + 'the', + 'cat', + 'went', + 'over', + 'the', + 'wall' + ]); + t.deepEqual(tokenize('That\'ll cause problems for the farmer\'s pigs'), [ + 'that\'ll', + 'cause', + 'problems', + 'for', + 'the', + 'farmer\'s', + 'pigs' + ]); t.end(); }); -test('diacritic', function (t) { - t.deepEqual( - tokenize('This approach is naïve.'), - ['this', 'approach', 'is', 'naïve'] - ); - t.deepEqual( - tokenize('The puppy bowl team was very coöperative.'), - ['the', 'puppy', 'bowl', 'team', 'was', 'very', 'coöperative'] - ); - t.deepEqual( - tokenize('The soufflé was delicious!'), - ['the', 'soufflé', 'was', 'delicious'] - ); +test('diacritic', t => { + t.deepEqual(tokenize('This approach is naïve.'), [ + 'this', + 'approach', + 'is', + 'naïve' + ]); + t.deepEqual(tokenize('The puppy bowl team was very coöperative.'), [ + 'the', + 'puppy', + 'bowl', + 'team', + 'was', + 'very', + 'coöperative' + ]); + t.deepEqual(tokenize('The soufflé was delicious!'), [ + 'the', + 'soufflé', + 'was', + 'delicious' + ]); t.end(); }); From 2b7ce07ffea7fd8612a0bb2b9aeb461dd6027d84 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 21:16:51 -0300 Subject: [PATCH 04/10] refactor: Rewrite lib syntax --- lib/index.js | 85 +++++++++++----------- lib/language-processor.js | 143 ++++++++++++++++++-------------------- lib/tokenize.js | 13 ++-- 3 files changed, 116 insertions(+), 125 deletions(-) diff --git a/lib/index.js b/lib/index.js index ff8a672..f950ac6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,42 +1,36 @@ -var tokenize = require('./tokenize'); -var languageProcessor = require('./language-processor'); +const tokenize = require('./tokenize'); +const languageProcessor = require('./language-processor'); /** - * Constructor + * Represents a Sentiment. + * @constructor * @param {Object} options - Instance options */ -var Sentiment = function (options) { +const Sentiment = function(options) { this.options = options; }; /** - * Registers the specified language - * - * @param {String} languageCode - * - Two-digit code for the language to register - * @param {Object} language - * - The language module to register + * Registers the specified language. + * @param {string} languageCode - Two-digit code for the language to register. + * @param {object} language - The language module to register. */ -Sentiment.prototype.registerLanguage = function (languageCode, language) { +Sentiment.prototype.registerLanguage = (languageCode, language) => { languageProcessor.addLanguage(languageCode, language); }; /** * Performs sentiment analysis on the provided input 'phrase'. - * - * @param {String} phrase - * - Input phrase - * @param {Object} opts - * - Options - * @param {Object} opts.language - * - Input language code (2 digit code), defaults to 'en' - * @param {Object} opts.extras - * - Optional sentiment additions to AFINN (hash k/v pairs) - * @param {function} callback - * - Optional callback - * @return {Object} + * @param {string} phrase - Input phrase + * @param {object} opts - Options + * @param {object} opts.language - Input language code (2 digit code), defaults + * to 'en' + * @param {object} opts.extras - Optional sentiment additions to AFINN + * (hash k/v pairs) + * @param {function} callback - Optional callback + * @return {object} */ -Sentiment.prototype.analyze = function (phrase, opts, callback) { +Sentiment.prototype.analyze = (phrase, opts, callback) => { // Parse arguments if (typeof phrase === 'undefined') phrase = ''; if (typeof opts === 'function') { @@ -45,8 +39,8 @@ Sentiment.prototype.analyze = function (phrase, opts, callback) { } opts = opts || {}; - var languageCode = opts.language || 'en'; - var labels = languageProcessor.getLabels(languageCode); + const languageCode = opts.language || 'en'; + let labels = languageProcessor.getLabels(languageCode); // Merge extra labels if (typeof opts.extras === 'object') { @@ -54,40 +48,45 @@ Sentiment.prototype.analyze = function (phrase, opts, callback) { } // Storage objects - var tokens = tokenize(phrase), - score = 0, - words = [], - positive = [], - negative = []; + let tokens = tokenize(phrase); + let score = 0; + const words = []; + const positive = []; + const negative = []; // Iterate over tokens - var i = tokens.length; + let i = tokens.length; while (i--) { - var obj = tokens[i]; + const obj = tokens[i]; if (!labels.hasOwnProperty(obj)) continue; words.push(obj); // Apply scoring strategy - var tokenScore = labels[obj]; + let tokenScore = labels[obj]; // eslint-disable-next-line max-len - tokenScore = languageProcessor.applyScoringStrategy(languageCode, tokens, i, tokenScore); + tokenScore = languageProcessor.applyScoringStrategy( + languageCode, + tokens, + i, + tokenScore + ); if (tokenScore > 0) positive.push(obj); if (tokenScore < 0) negative.push(obj); score += tokenScore; } - var result = { - score: score, - comparative: tokens.length > 0 ? score / tokens.length : 0, - tokens: tokens, - words: words, - positive: positive, - negative: negative + const result = { + score, + comparative: tokens.length > 0 ? score / tokens.length : 0, + tokens, + words, + positive, + negative }; // Handle optional async interface if (typeof callback === 'function') { - process.nextTick(function () { + process.nextTick(function() { callback(null, result); }); } else { diff --git a/lib/language-processor.js b/lib/language-processor.js index fe373de..4ac6a5b 100644 --- a/lib/language-processor.js +++ b/lib/language-processor.js @@ -1,89 +1,80 @@ -var emojis = require('../build/emoji.json'); - +const emojis = require('../build/emoji.json'); // English is loaded by default -var enLanguage = require('../languages/en/index'); +const enLanguage = require('../languages/en/index'); // Add emojis Object.assign(enLanguage.labels, emojis); // Cache loaded languages -var languages = { +const languages = { en: enLanguage }; -module.exports = { - - /** - * Registers the specified language - * - * @param {String} languageCode - * - Two-digit code for the language to register - * @param {Object} language - * - The language module to register - */ - addLanguage: function (languageCode, language) { - if (!language.labels) { - throw new Error('language.labels must be defined!'); - } - // Add emojis - Object.assign(language.labels, emojis); - languages[languageCode] = language; - }, - - /** - * Retrieves a language object from the cache, - * or tries to load it from the set of supported languages - * - * @param {String} languageCode - Two-digit code for the language to fetch - */ - getLanguage: function(languageCode) { - if (!languageCode) { - // Default to english if no language was specified - return languages.en; - } - if (!languages[languageCode]) { - // Try to load specified language - try { - // eslint-disable-next-line max-len - var language = require('../languages/' + languageCode + '/index'); - // Add language to in-memory cache - this.addLanguage(languageCode, language); - } catch (err) { - throw new Error('No language found: ' + languageCode); - } +const defaultScoringStrategy = { + apply: (tokens, cursor, tokenScore) => tokenScore +}; +/** + * Registers the specified language. + * @param {string} languageCode - Two-digit code for the language to register. + * @param {object} language - The language module to register. + * @return {undefined} + */ +const addLanguage = (languageCode, language) => { + if (!language.labels) { + throw new Error('language.labels must be defined!'); + } + // Add emojis + Object.assign(language.labels, emojis); + languages[languageCode] = language; +}; +/** + * Retrieves a language object from the cache, or tries to load it from the set + * of supported languages. + * @param {string} languageCode - Two-digit code for the language to fetch. + * @return {undefined} + */ +const getLanguage = languageCode => { + if (!languageCode) { + // Default to english if no language was specified + return languages.en; + } + if (!languages[languageCode]) { + // Try to load specified language + try { + const language = require('../languages/' + languageCode + '/index'); + // Add language to in-memory cache + addLanguage(languageCode, language); + } catch (err) { + throw new Error('No language found: ' + languageCode); } - return languages[languageCode]; - }, - - /** - * Returns AFINN-165 weighted labels for the specified language - * - * @param {String} languageCode - Two-digit language code - * @return {Object} - */ - getLabels: function(languageCode) { - var language = this.getLanguage(languageCode); - return language.labels; - }, - - /** - * Applies a scoring strategy for the current token - * - * @param {String} languageCode - Two-digit language code - * @param {Array} tokens - Tokens of the phrase to analyze - * @param {int} cursor - Cursor of the current token being analyzed - * @param {int} tokenScore - The score of the current token being analyzed - */ - applyScoringStrategy: function(languageCode, tokens, cursor, tokenScore) { - var language = this.getLanguage(languageCode); - // Fallback to default strategy if none was specified - // eslint-disable-next-line max-len - var scoringStrategy = language.scoringStrategy || defaultScoringStrategy; - return scoringStrategy.apply(tokens, cursor, tokenScore); } + return languages[languageCode]; +}; +/** + * Returns AFINN-165 weighted labels for the specified language. + * @param {string} languageCode - Two-digit language code. + * @return {object} + */ +const getLabels = languageCode => { + const language = getLanguage(languageCode); + return language.labels; +}; +/** + * Applies a scoring strategy for the current token. + * @param {string} languageCode - Two-digit language code. + * @param {array} Tokens - Tokens of the phase to analyze. + * @param {int} cursor - Cursor of the current token being analyzed. + * @param {int} tokenScore - The score of the current token being analyzed. + */ +const applyScoringStrategy = (languageCode, tokens, cursor, tokenScore) => { + const language = getLanguage(languageCode); + // Fallback to default strategy if none was specified + const scoringStrategy = language.scoringStrategy || defaultScoringStrategy; + return scoringStrategy.apply(tokens, cursor, tokenScore); }; -var defaultScoringStrategy = { - apply: function(tokens, cursor, tokenScore) { - return tokenScore; - } +module.exports = { + addLanguage, + getLanguage, + getLabels, + applyScoringStrategy }; diff --git a/lib/tokenize.js b/lib/tokenize.js index 6ac47a4..6b25d98 100644 --- a/lib/tokenize.js +++ b/lib/tokenize.js @@ -1,14 +1,15 @@ /*eslint no-useless-escape: "off"*/ /** - * Remove special characters and return an array of tokens (words). - * @param {string} input Input string - * @return {array} Array of tokens + * Removes special characters and return an array of tokens (words). + * @param {string} input Input string. + * @return {array} Array of tokens. */ -module.exports = function(input) { - return input +const tokenize = input => + input .toLowerCase() .replace(/\n/g, ' ') .replace(/[.,\/#!$%\^&\*;:{}=_`\"~()]/g, '') .split(' '); -}; + +module.exports = tokenize; From f8c85c9ac0e4bb4bc46549d5335fdfd19605f71a Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 22:48:05 -0300 Subject: [PATCH 05/10] refactor: Rewrite index.js to class syntax --- lib/index.js | 166 +++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/lib/index.js b/lib/index.js index f950ac6..05e4e9a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,97 +1,97 @@ const tokenize = require('./tokenize'); const languageProcessor = require('./language-processor'); -/** - * Represents a Sentiment. - * @constructor - * @param {Object} options - Instance options - */ -const Sentiment = function(options) { - this.options = options; -}; - -/** - * Registers the specified language. - * @param {string} languageCode - Two-digit code for the language to register. - * @param {object} language - The language module to register. - */ -Sentiment.prototype.registerLanguage = (languageCode, language) => { - languageProcessor.addLanguage(languageCode, language); -}; - -/** - * Performs sentiment analysis on the provided input 'phrase'. - * @param {string} phrase - Input phrase - * @param {object} opts - Options - * @param {object} opts.language - Input language code (2 digit code), defaults - * to 'en' - * @param {object} opts.extras - Optional sentiment additions to AFINN - * (hash k/v pairs) - * @param {function} callback - Optional callback - * @return {object} - */ -Sentiment.prototype.analyze = (phrase, opts, callback) => { - // Parse arguments - if (typeof phrase === 'undefined') phrase = ''; - if (typeof opts === 'function') { - callback = opts; - opts = {}; +class Sentiment { + /** + * Represents a Sentiment. + * @constructor + * @param {Object} options - Instance options. + */ + constructor(options) { + this.options = options; + } + /** + * Registers the specified language. + * @param {string} languageCode - Two-digit code for the language to + * register. + * @param {object} language - The language module to register. + */ + registerLanguage(languageCode, language) { + languageProcessor.addLanguage(languageCode, language); } - opts = opts || {}; + /** + * Performs sentiment analysis on the provided input 'phrase'. + * @param {string} phrase - Input phrase + * @param {object} opts - Options + * @param {object} opts.language - Input language code (2 digit code), + * defaults to 'en' + * @param {object} opts.extras - Optional sentiment additions to AFINN + * (hash k/v pairs) + * @param {function} callback - Optional callback + * @return {object} + */ + analyze(phrase = '', opts = { language: 'en' }, callback) { + // Storage objects + const tokens = tokenize(phrase); + let score = 0; + let words = []; + let positive = []; + let negative = []; - const languageCode = opts.language || 'en'; - let labels = languageProcessor.getLabels(languageCode); + // Parse arguments + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } - // Merge extra labels - if (typeof opts.extras === 'object') { - labels = Object.assign(labels, opts.extras); - } + const languageCode = opts.language; + let labels = languageProcessor.getLabels(languageCode); - // Storage objects - let tokens = tokenize(phrase); - let score = 0; - const words = []; - const positive = []; - const negative = []; + // Merge extra labels + if (typeof opts.extras === 'object') { + labels = Object.assign(labels, opts.extras); + } - // Iterate over tokens - let i = tokens.length; - while (i--) { - const obj = tokens[i]; - if (!labels.hasOwnProperty(obj)) continue; - words.push(obj); + // Iterate over tokens + let i = tokens.length; + while (i--) { + const obj = tokens[i]; + if (!labels.hasOwnProperty(obj)) continue; + words = words.concat(obj); + // Apply scoring strategy + const tokenScore = languageProcessor.applyScoringStrategy( + languageCode, + tokens, + i, + labels[obj] + ); + if (tokenScore > 0) { + positive = positive.concat(obj); + } + if (tokenScore < 0) { + negative = negative.concat(obj); + } + score += tokenScore; + } - // Apply scoring strategy - let tokenScore = labels[obj]; - // eslint-disable-next-line max-len - tokenScore = languageProcessor.applyScoringStrategy( - languageCode, + const result = { + score, + comparative: tokens.length > 0 ? score / tokens.length : 0, tokens, - i, - tokenScore - ); - if (tokenScore > 0) positive.push(obj); - if (tokenScore < 0) negative.push(obj); - score += tokenScore; - } - - const result = { - score, - comparative: tokens.length > 0 ? score / tokens.length : 0, - tokens, - words, - positive, - negative - }; + words, + positive, + negative + }; - // Handle optional async interface - if (typeof callback === 'function') { - process.nextTick(function() { - callback(null, result); - }); - } else { - return result; + // Handle optional async interface + if (typeof callback === 'function') { + process.nextTick(() => { + callback(null, result); + }); + } else { + return result; + } } -}; +} module.exports = Sentiment; From 03fe1e847dec0cf14986cf9558c3a93206398424 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Thu, 14 Jun 2018 22:58:08 -0300 Subject: [PATCH 06/10] refactor: Minor changes --- build/build.js | 4 ++-- languages/en/index.js | 7 +++++-- languages/en/scoring-strategy.js | 20 +++++++++++--------- lib/language-processor.js | 4 ++-- test/fixtures/fuzz.js | 8 +++++--- test/unit/spec.js | 1 - 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/build/build.js b/build/build.js index 6c5749b..24016f8 100644 --- a/build/build.js +++ b/build/build.js @@ -17,7 +17,7 @@ const processEmoji = async hash => { // Split data by new line const lines = data.split(/\n/); // Iterate over dataset and add to hash - for (var i in lines) { + for (const i in lines) { const line = lines[i].split(','); // Validate line if (i === '0' || i === 0 || line.length !== 9) continue; @@ -65,7 +65,7 @@ const build = async () => { hash = await processEmoji(hash); hash = await finish(hash); process.stderr.write( - 'Complete: ' + Object.keys(hash).length + ' entries.\n' + `Complete: ${Object.keys(hash).length} entries.\n` ); } catch (e) { throw new Error(e); diff --git a/languages/en/index.js b/languages/en/index.js index 46e0f91..207c1f6 100644 --- a/languages/en/index.js +++ b/languages/en/index.js @@ -1,4 +1,7 @@ +const labels = require('./labels.json'); +const scoringStrategy = require('./scoring-strategy'); + module.exports = { - labels: require('./labels.json'), - scoringStrategy: require('./scoring-strategy') + labels, + scoringStrategy }; diff --git a/languages/en/scoring-strategy.js b/languages/en/scoring-strategy.js index d9a09ca..e4c05b3 100644 --- a/languages/en/scoring-strategy.js +++ b/languages/en/scoring-strategy.js @@ -1,13 +1,15 @@ -var negators = require('./negators.json'); +const negators = require('./negators.json'); -module.exports = { - apply: function(tokens, cursor, tokenScore) { - if (cursor > 0) { - var prevtoken = tokens[cursor - 1]; - if (negators[prevtoken]) { - tokenScore = -tokenScore; - } +const apply = (tokens, cursor, tokenScore) => { + if (cursor > 0) { + const prevtoken = tokens[cursor - 1]; + if (negators[prevtoken]) { + tokenScore = -tokenScore; } - return tokenScore; } + return tokenScore; +}; + +module.exports = { + apply }; diff --git a/lib/language-processor.js b/lib/language-processor.js index 4ac6a5b..158c1d2 100644 --- a/lib/language-processor.js +++ b/lib/language-processor.js @@ -55,8 +55,8 @@ const getLanguage = languageCode => { * @return {object} */ const getLabels = languageCode => { - const language = getLanguage(languageCode); - return language.labels; + const { labels } = getLanguage(languageCode); + return labels; }; /** * Applies a scoring strategy for the current token. diff --git a/test/fixtures/fuzz.js b/test/fixtures/fuzz.js index 064b741..1b0b485 100644 --- a/test/fixtures/fuzz.js +++ b/test/fixtures/fuzz.js @@ -6,7 +6,7 @@ const createRandomWord = length => { let word = ''; // Create word - for (var i = 0; i < length / 2; i++) { + for (let i = 0; i < length / 2; i++) { const randConsonant = consonants[rand(consonants.length)]; const randVowel = vowels[rand(vowels.length)]; @@ -16,10 +16,12 @@ const createRandomWord = length => { return word; }; -module.exports = length => { +const fuzz = length => { const words = []; - for (var i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { words.push(createRandomWord(rand(20))); } return words.join(' '); }; + +module.exports = fuzz; diff --git a/test/unit/spec.js b/test/unit/spec.js index 7dd8154..6b5176f 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -10,7 +10,6 @@ test('module', t => { test('interface', t => { t.type(sentiment, 'object', 'instance is an object'); t.type(sentiment.analyze, 'function', 'sentiment.analyze is a function'); - // eslint-disable-next-line max-len t.type( sentiment.registerLanguage, 'function', From 3792bdc1b65401908b1560f4137af38710a02855 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Tue, 19 Jun 2018 15:00:53 -0300 Subject: [PATCH 07/10] style: Format package.json --- package.json | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index fe5fa3c..f997c9c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,12 @@ "type": "git", "url": "https://github.com/thisandagain/sentiment.git" }, - "keywords": ["sentiment", "analysis", "nlp", "sentiment analysis"], + "keywords": [ + "sentiment", + "analysis", + "nlp", + "sentiment analysis" + ], "main": "./lib/index.js", "scripts": { "build": "node ./build/build.js", @@ -18,10 +23,8 @@ "test:integration": "tap test/integration/*.js", "test:benchmark": "node ./test/benchmark/performance.js", "test:validate": "node ./test/benchmark/validate.js", - "test:coverage": - "tap './test/{integration,unit}/*.js' --coverage --coverage-report=lcov", - "test": - "npm run test:lint && npm run test:unit && npm run test:integration && npm run test:benchmark && npm run test:validate" + "test:coverage": "tap './test/{integration,unit}/*.js' --coverage --coverage-report=lcov", + "test": "npm run test:lint && npm run test:unit && npm run test:integration && npm run test:benchmark && npm run test:validate" }, "devDependencies": { "Sentimental": "1.0.1", @@ -33,4 +36,4 @@ "engines": { "node": ">=8.0" } -} +} \ No newline at end of file From 8bb960e5a27e39241a1ab04e44dffd17e72e5654 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Tue, 19 Jun 2018 17:36:31 -0300 Subject: [PATCH 08/10] refactor: Simplify nesting; Minor improvements --- build/build.js | 78 ++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/build/build.js b/build/build.js index 24016f8..db06826 100644 --- a/build/build.js +++ b/build/build.js @@ -6,69 +6,61 @@ const EMOJI_PATH = path.resolve(__dirname, 'Emoji_Sentiment_Data_v1.0.csv'); const RESULT_PATH = path.resolve(__dirname, 'emoji.json'); /** - * Read emoji data from original format (CSV). + * Reads emoji data from original format (CSV). * @param {object} hash Result hash. * @return {object} hash Result hash. */ -const processEmoji = async hash => { +const processEmoji = async () => { + let hash = {}; // Read file - try { - const data = await fs.readFileSync(EMOJI_PATH, 'utf8'); - // Split data by new line - const lines = data.split(/\n/); - // Iterate over dataset and add to hash - for (const i in lines) { - const line = lines[i].split(','); - // Validate line - if (i === '0' || i === 0 || line.length !== 9) continue; - // ^ Label ^ Label ^ Invalid + const data = await fs.readFileSync(EMOJI_PATH, 'utf8'); + // Split data by new line + const lines = data.split(/\n/); + // Iterate over dataset and add to hash + for (const i in lines) { + const line = lines[i].split(','); + // Validate line + if (i === '0' || i === 0 || line.length !== 9) continue; + // ^ Label ^ Label ^ Invalid - // Establish sentiment value - const emoji = String.fromCodePoint(line[1]); - const occurences = line[2]; - const negCount = line[4]; - const posCount = line[6]; - const score = posCount / occurences - negCount / occurences; - const sentiment = Math.floor(5 * score); + // Establish sentiment value + const emoji = String.fromCodePoint(line[1]); + const occurences = line[2]; + const negCount = line[4]; + const posCount = line[6]; + const score = posCount / occurences - negCount / occurences; + const sentiment = Math.floor(5 * score); - // Validate score - if (Number.isNaN(sentiment) || sentiment === 0) continue; + // Validate score + if (Number.isNaN(sentiment) || sentiment === 0) continue; - // Add to hash - hash[emoji] = sentiment; - } - return hash; - } catch (e) { - throw new Error(e); + // Add to hash + hash[emoji] = sentiment; } + return hash; }; /** - * Write sentiment score hash to disk. + * Writes sentiment score hash to disk. * @param {object} hash Result hash * @return {object} hash Result hash */ -const finish = async hash => { +const writeJSON = async hash => { const result = JSON.stringify(hash, null, 4); - try { - await fs.writeFileSync(RESULT_PATH, result); - return hash; - } catch (e) { - throw new Error(e); - } + await fs.writeFileSync(RESULT_PATH, result); + process.stderr.write(`Complete: ${Object.keys(hash).length} entries.\n`); }; -// Execute build process +/** + * Executes build process. + * @return {undefined} + */ const build = async () => { try { - let hash = {}; - hash = await processEmoji(hash); - hash = await finish(hash); - process.stderr.write( - `Complete: ${Object.keys(hash).length} entries.\n` - ); + const hash = await processEmoji(); + writeJSON(hash); } catch (e) { - throw new Error(e); + console.log(e); } }; build(); From 330a687677d69bc67ecce79257c91e0e51ac2870 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Tue, 19 Jun 2018 17:37:26 -0300 Subject: [PATCH 09/10] refactor: Add template literals --- lib/language-processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/language-processor.js b/lib/language-processor.js index 158c1d2..504fd74 100644 --- a/lib/language-processor.js +++ b/lib/language-processor.js @@ -40,7 +40,7 @@ const getLanguage = languageCode => { if (!languages[languageCode]) { // Try to load specified language try { - const language = require('../languages/' + languageCode + '/index'); + const language = require(`../languages/${languageCode}/index`); // Add language to in-memory cache addLanguage(languageCode, language); } catch (err) { From 68df7ebfe17d5dbb29ca2f147528d98ae9240102 Mon Sep 17 00:00:00 2001 From: Alexandre Silva Date: Tue, 19 Jun 2018 17:54:41 -0300 Subject: [PATCH 10/10] refactor: Remove console statement; Minor fix --- build/build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.js b/build/build.js index db06826..23cb764 100644 --- a/build/build.js +++ b/build/build.js @@ -48,7 +48,7 @@ const processEmoji = async () => { const writeJSON = async hash => { const result = JSON.stringify(hash, null, 4); await fs.writeFileSync(RESULT_PATH, result); - process.stderr.write(`Complete: ${Object.keys(hash).length} entries.\n`); + process.stdout.write(`Complete: ${Object.keys(hash).length} entries.\n`); }; /** @@ -60,7 +60,7 @@ const build = async () => { const hash = await processEmoji(); writeJSON(hash); } catch (e) { - console.log(e); + process.stderr.write(`${e.toString()}\n`); } }; build();