Skip to content

Commit

Permalink
refactor: major API changes (#42)
Browse files Browse the repository at this point in the history
* Initial work

* Still return words

* Update README

* Change API

* Update README

* Address suggestions

Signed-off-by: Josh-Cena <[email protected]>

* Update README.md
  • Loading branch information
Josh-Cena authored Oct 4, 2021
1 parent 2a2d8aa commit a14a217
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 185 deletions.
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,68 @@ const readingTime = require('reading-time');
const stats = readingTime(text);
// ->
// stats: {
// text: '1 min read',
// minutes: 1,
// time: 60000,
// words: 200
// words: {total: 200}
// }
console.log(`The reading time is: ${stats.minutes} min`);
```

### Stream

```javascript
const {ReadingTimeStream} = require('reading-time');
const {ReadingTimeStream, readingTimeWithCount} = require('reading-time');

const analyzer = new ReadingTimeStream();
fs.createReadStream('foo')
.pipe(analyzer)
.on('data', stats => {
// ...
.on('data', (count) => {
console.log(`The reading time is: ${readingTimeWithCount(count).minutes} min`);
});
```

## API

`readingTime(text, options?)`
### `readingTime(text, options?)`

Returns an object with `minutes`, `time` (in milliseconds), and `words`.

```ts
type ReadingTimeResults = {
minutes: number;
time: number;
words: WordCountStats;
};
```

- `text`: the text to analyze
- options (optional)
- `options.wordsPerMinute`: (optional) the words per minute an average reader can read (default: 200)
- `options.wordBound`: (optional) a function that returns a boolean value depending on if a character is considered as a word bound (default: spaces, new lines and tabs)

### `countWords(text, options?)`

Returns an object representing the word count stats:

```ts
type WordCountStats = {
total: number;
};
```

- `text`: the text to analyze
- options (optional)
- `options.wordBound`: (optional) a function that returns a boolean value depending on if a character is considered as a word bound (default: spaces, new lines and tabs)

### `readingTimeWithCount(words, options?)`

Returns an object with `minutes` (rounded minute stats) and `time` (exact time in milliseconds).

- `words`: the word count stats
- options (optional)
- `options.wordsPerMinute`: (optional) the words per minute an average reader can read (default: 200)
- `options.wordBound`: (optional) a function that returns a boolean value depending on if a character is considered as a word bound (default: spaces, new lines and tabulations)

Note that `readingTime(text, options) === readingTimeWithCount(countWords(text, options), options)`.

## Help wanted!

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"words per minute"
],
"author": "Nicolas Gryman <[email protected]> (http://ngryman.sh)",
"contributors": ["Joshua Chen <[email protected]> (https://joshcena.com)"],
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.2.21",
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import readingTime from './reading-time'
import readingTime, { countWords, readingTimeWithCount } from './reading-time'
import ReadingTimeStream from './stream'

// This part is to make TS happy
export { ReadingTimeStream }
export { ReadingTimeStream, countWords, readingTimeWithCount }
export default readingTime

// Wacky way to support const readingTime = require('reading-time') :(
// Basically we can't use ES import/export anymore because re-assigning module.exports
// decouples it from the exports object, which TS export compiles to
module.exports = readingTime
module.exports.default = readingTime
module.exports.countWords = countWords
module.exports.readingTimeWithCount = readingTimeWithCount
module.exports.ReadingTimeStream = ReadingTimeStream
module.exports.__esModule = true
34 changes: 21 additions & 13 deletions src/reading-time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* MIT Licensed
*/

import type { Options, ReadTimeResults } from 'reading-time'
import type { Options, ReadingTimeStats, WordCountStats, ReadingTimeResult } from 'reading-time'

type WordBoundFunction = Options['wordBound']

Expand Down Expand Up @@ -58,12 +58,9 @@ const isPunctuation: WordBoundFunction = (c) => {
)
}

function readingTime(text: string, options: Options = {}): ReadTimeResults {
export function countWords(text: string, options: Options = {}): WordCountStats {
let words = 0, start = 0, end = text.length - 1
const {
wordsPerMinute = 200,
wordBound: isWordBound = isAnsiWordBound
} = options
const { wordBound: isWordBound = isAnsiWordBound } = options

// fetch bounds
while (isWordBound(text[start])) start++
Expand Down Expand Up @@ -94,20 +91,31 @@ function readingTime(text: string, options: Options = {}): ReadTimeResults {
}
}
}
return { total: words }
}

export function readingTimeWithCount(
words: WordCountStats,
options: Options = {}
): ReadingTimeStats {
const { wordsPerMinute = 200 } = options
// reading time stats
const minutes = words / wordsPerMinute
// Math.round used to resolve floating point funkyness
const minutes = words.total / wordsPerMinute
// Math.round used to resolve floating point funkiness
// http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
const time = Math.round(minutes * 60 * 1000)
const displayed = Math.ceil(parseFloat(minutes.toFixed(2)))

return {
text: displayed + ' min read',
minutes,
time,
words
minutes: displayed,
time
}
}

export default readingTime
export default function readingTime(text: string, options: Options = {}): ReadingTimeResult {
const words = countWords(text, options)
return {
...readingTimeWithCount(words, options),
words
}
}
23 changes: 6 additions & 17 deletions src/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,28 @@
* MIT Licensed
*/

import readingTime from './reading-time'
import { countWords } from './reading-time'
import { Transform, TransformCallback } from 'stream'
import type { Options, ReadTimeResults } from 'reading-time'
import type { Options, WordCountStats } from 'reading-time'

class ReadingTimeStream extends Transform {
options: Options;
stats: ReadTimeResults;
stats: WordCountStats;

constructor(options: Options = {}) {
super({ objectMode: true })

this.options = options
this.stats = {
text: '',
minutes: 0,
time: 0,
words: 0
}
this.stats = { total: 0 }
}

_transform(chunk: Buffer, encoding: BufferEncoding, callback: TransformCallback): void {
const stats = readingTime(chunk.toString(encoding), this.options)

this.stats.minutes += stats.minutes
this.stats.time += stats.time
this.stats.words += stats.words

const stats = countWords(chunk.toString(encoding), this.options)
this.stats.total += stats.total
callback()
}

_flush(callback: TransformCallback): void {
this.stats.text = Math.ceil(parseFloat(this.stats.minutes.toFixed(2))) + ' min read'

this.push(this.stats)
callback()
}
Expand Down
20 changes: 14 additions & 6 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
declare module 'reading-time' {
import { Transform, TransformCallback } from 'stream'

export interface Options {
export type Options = {
wordBound?: (char: string) => boolean;
wordsPerMinute?: number;
}

export interface ReadTimeResults {
text: string;
export type ReadingTimeStats = {
time: number;
words: number;
minutes: number;
}

export type WordCountStats = {
total: number;
}

export class ReadingTimeStream extends Transform {
stats: ReadTimeResults;
stats: WordCountStats;
options: Options;
constructor(options?: Options);
_transform: (chunk: Buffer, encoding: BufferEncoding, callback: TransformCallback) => void;
_flush: (callback: TransformCallback) => void;
}

export default function readingTime(text: string, options?: Options): ReadTimeResults
export type ReadingTimeResult = ReadingTimeStats & {
words: WordCountStats;
}

export function countWords(text: string, options?: Options): WordCountStats
export function readingTimeWithCount(words: WordCountStats, options?: Options): ReadingTimeStats
export default function readingTime(text: string, options?: Options): ReadingTimeResult
}
Loading

0 comments on commit a14a217

Please sign in to comment.