AFINN-based sentiment analysis for Node.js with multi-language support and custom scoring strategies
npx @tessl/cli install tessl/npm-sentiment@5.0.00
# Sentiment
1
2
Sentiment is a high-performance AFINN-based sentiment analysis library for Node.js that uses the AFINN-165 wordlist and Emoji Sentiment Ranking to analyze the emotional tone of text input. It provides comprehensive sentiment scoring with detailed breakdowns, supports multiple languages through a plugin system, and offers custom scoring strategies for handling negation and emphasis.
3
4
## Package Information
5
6
- **Package Name**: sentiment
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install sentiment`
10
11
## Core Imports
12
13
```javascript
14
const Sentiment = require('sentiment');
15
```
16
17
ES6 import (if using a transpiler):
18
19
```javascript
20
import Sentiment from 'sentiment';
21
```
22
23
## Basic Usage
24
25
```javascript
26
const Sentiment = require('sentiment');
27
const sentiment = new Sentiment();
28
29
// Analyze sentiment of text
30
const result = sentiment.analyze('Cats are totally amazing!');
31
console.log(result);
32
// {
33
// score: 4,
34
// comparative: 1,
35
// calculation: [{ amazing: 4 }],
36
// tokens: ['cats', 'are', 'totally', 'amazing'],
37
// words: ['amazing'],
38
// positive: ['amazing'],
39
// negative: []
40
// }
41
42
// Override AFINN wordlist
43
const customResult = sentiment.analyze('Cats are stupid.', {
44
extras: { 'cats': 5, 'stupid': -1 }
45
});
46
console.log(customResult.score); // 4 (5 + -1)
47
```
48
49
## Architecture
50
51
Sentiment analysis is built around several key components:
52
53
- **AFINN Wordlist**: Pre-scored word database with values from -5 (negative) to +5 (positive)
54
- **Emoji Support**: Sentiment rankings for emojis integrated into all languages
55
- **Tokenization**: Text preprocessing that splits input into individual words and emojis
56
- **Language System**: Pluggable language support with custom scoring strategies
57
- **Scoring Strategy**: Per-language logic for handling negation, emphasis, and context
58
59
## Capabilities
60
61
### Sentiment Analysis
62
63
Analyzes the emotional tone of input text and returns detailed sentiment metrics.
64
65
```javascript { .api }
66
/**
67
* Performs sentiment analysis on the provided input phrase
68
* @param {string} phrase - Input phrase to analyze
69
* @param {object} [opts] - Analysis options
70
* @param {string} [opts.language='en'] - Language code for analysis (defaults to 'en')
71
* @param {object} [opts.extras={}] - Additional word/score pairs to add or overwrite
72
* @param {function} [callback] - Optional callback function
73
* @returns {object} Sentiment analysis result
74
*/
75
analyze(phrase, opts, callback);
76
```
77
78
**Usage Examples:**
79
80
```javascript
81
const sentiment = new Sentiment();
82
83
// Basic analysis
84
const result = sentiment.analyze('I love sunny days!');
85
86
// With language specification
87
const frenchResult = sentiment.analyze('Je suis heureux', {
88
language: 'fr'
89
});
90
91
// With custom word overrides
92
const customResult = sentiment.analyze('This product is okay', {
93
extras: { 'okay': 2 }
94
});
95
96
// With callback (async)
97
sentiment.analyze('Hello world', (err, result) => {
98
console.log(result.score);
99
});
100
```
101
102
### Language Registration
103
104
Registers support for additional languages with custom word lists and scoring strategies.
105
106
```javascript { .api }
107
/**
108
* Registers a new language for sentiment analysis
109
* @param {string} languageCode - Two-digit language code (e.g., 'fr', 'es')
110
* @param {object} language - Language configuration object
111
* @param {object} language.labels - Word-to-score mapping for the language
112
* @param {object} [language.scoringStrategy] - Custom scoring strategy for the language
113
*/
114
registerLanguage(languageCode, language);
115
```
116
117
**Usage Examples:**
118
119
```javascript
120
const sentiment = new Sentiment();
121
122
// Register French language
123
sentiment.registerLanguage('fr', {
124
labels: {
125
'bon': 3,
126
'mauvais': -3,
127
'excellent': 5,
128
'horrible': -5
129
}
130
});
131
132
// Register with custom scoring strategy
133
sentiment.registerLanguage('es', {
134
labels: {
135
'bueno': 3,
136
'malo': -3
137
},
138
scoringStrategy: {
139
apply: function(tokens, cursor, tokenScore) {
140
// Custom negation handling for Spanish
141
if (cursor > 0 && tokens[cursor - 1] === 'no') {
142
return -tokenScore;
143
}
144
return tokenScore;
145
}
146
}
147
});
148
149
// Use registered language
150
const result = sentiment.analyze('Très bon!', { language: 'fr' });
151
```
152
153
## Types
154
155
### Sentiment Constructor
156
157
```javascript { .api }
158
/**
159
* Creates a new Sentiment analyzer instance
160
* @param {object} [options] - Configuration options (none currently supported)
161
*/
162
function Sentiment(options);
163
```
164
165
### Analysis Result
166
167
```javascript { .api }
168
interface AnalysisResult {
169
/** Total sentiment score calculated by summing individual word scores */
170
score: number;
171
/** Comparative score (score divided by number of tokens) */
172
comparative: number;
173
/** Array of objects showing which words contributed to the score */
174
calculation: Array<{[word: string]: number}>;
175
/** All tokens (words/emojis) found in the input text */
176
tokens: string[];
177
/** Words from input that were found in the sentiment wordlist */
178
words: string[];
179
/** Words with positive sentiment scores */
180
positive: string[];
181
/** Words with negative sentiment scores */
182
negative: string[];
183
}
184
```
185
186
### Language Object
187
188
```javascript { .api }
189
interface LanguageObject {
190
/** Word-to-score mapping where keys are words and values are sentiment scores (-5 to +5) */
191
labels: {[word: string]: number};
192
/** Optional custom scoring strategy for handling context and negation */
193
scoringStrategy?: ScoringStrategy;
194
}
195
196
interface ScoringStrategy {
197
/**
198
* Applies contextual scoring logic to individual tokens
199
* @param {string[]} tokens - All tokens in the phrase being analyzed
200
* @param {number} cursor - Index of the current token being scored
201
* @param {number} tokenScore - Base sentiment score for the current token
202
* @returns {number} Modified sentiment score for the token
203
*/
204
apply(tokens: string[], cursor: number, tokenScore: number): number;
205
}
206
```
207
208
### Analysis Options
209
210
```javascript { .api }
211
interface AnalysisOptions {
212
/** Two-digit language code for analysis (defaults to 'en') */
213
language?: string;
214
/** Additional word/score pairs to add or override in the wordlist */
215
extras?: {[word: string]: number};
216
}
217
```
218
219
## Error Handling
220
221
The sentiment library handles several error conditions:
222
223
- **Invalid language code**: Throws `Error: No language found: <code>` when requesting an unregistered language
224
- **Missing language labels**: Throws `Error: language.labels must be defined!` when registering a language without required labels
225
- **Malformed input**: Gracefully handles undefined, null, or non-string input by treating it as empty string
226
227
**Example:**
228
229
```javascript
230
const sentiment = new Sentiment();
231
232
try {
233
// This will throw an error
234
sentiment.analyze('Hello', { language: 'xx' });
235
} catch (error) {
236
console.log(error.message); // "No language found: xx"
237
}
238
239
try {
240
// This will throw an error
241
sentiment.registerLanguage('fr', { /* missing labels */ });
242
} catch (error) {
243
console.log(error.message); // "language.labels must be defined!"
244
}
245
```