Skip to content

Commit 3167e32

Browse files
committed
.
0 parents  commit 3167e32

File tree

12 files changed

+514
-0
lines changed

12 files changed

+514
-0
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

.github/workflows/bb.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: bb
2+
on:
3+
issues:
4+
types: [opened, reopened, edited, closed, labeled, unlabeled]
5+
pull_request_target:
6+
types: [opened, reopened, edited, closed, labeled, unlabeled]
7+
jobs:
8+
main:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: unifiedjs/beep-boop-beta@main
12+
with:
13+
repo-token: ${{secrets.GITHUB_TOKEN}}

.github/workflows/main.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: main
2+
on:
3+
- pull_request
4+
- push
5+
jobs:
6+
main:
7+
name: ${{matrix.node}}
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- uses: dcodeIO/setup-node-nvm@master
12+
with:
13+
node-version: ${{matrix.node}}
14+
- run: npm install
15+
- run: npm test
16+
- uses: codecov/codecov-action@v1
17+
strategy:
18+
matrix:
19+
node:
20+
- lts/erbium
21+
- node

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
*.d.ts
3+
*.log
4+
coverage/
5+
node_modules/
6+
yarn.lock

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
coverage/
2+
*.md

index.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @typedef {import('hast').Root} Root
3+
* @typedef {import('hast').Content} Content
4+
* @typedef {Root|Content} Node
5+
*
6+
* @typedef Options
7+
* @property {number} [age=16]
8+
* Target age group.
9+
* This is the age your target audience was still in school.
10+
* Set it to 18 if you expect all readers to have finished high school,
11+
* 21 if you expect your readers to all be college graduates, etc.
12+
*/
13+
14+
import {toText} from 'hast-util-to-text'
15+
import readabilityScores from 'readability-scores'
16+
// @ts-expect-error: untyped.
17+
import median from 'compute-median'
18+
19+
// See source [1].
20+
const firstGradeAge = 5
21+
const highschoolGraduationAge = 18
22+
const graduationAge = 22
23+
24+
// See source [2].
25+
// Note that different other algorithms vary between 200, 230, 270, and 280.
26+
// 228 seems to at least be based in research.
27+
const reasonableWpm = 228
28+
const reasonableWpmMax = 340
29+
30+
// See source [3].
31+
const addedWpmPerGrade = 14
32+
const baseWpm =
33+
reasonableWpm - (highschoolGraduationAge - firstGradeAge) * addedWpmPerGrade
34+
35+
const accuracy = 1e6
36+
37+
/**
38+
* Utility to estimate the reading time, taking readability of the document and
39+
* a target age group into account.
40+
*
41+
* * [1] For more info on US education/grade levels, see:
42+
* <https://en.wikipedia.org/wiki/Educational_stage#United_States>.
43+
* * [2] For the wpm of people reading English, see:
44+
* <https://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension>
45+
* * [3] For information on reading rate, including how grade levels affect
46+
* them, see: <https://en.wikipedia.org/wiki/Reading#Reading_rate>.
47+
*
48+
* And some more background info/history and a few insight on where this comes
49+
* from, see: <https://martech.org/estimated-reading-times-increase-engagement/>.
50+
*
51+
* @param {Node} tree
52+
* Content to estimate.
53+
* @param {Options} [options]
54+
* Configuration.
55+
* @returns {number}
56+
* Estimated reading time in minutes.
57+
*/
58+
export function readingTime(tree, options = {}) {
59+
// Cap an age to a reasonable and meaningful age in school.
60+
const targetAge = Math.min(
61+
graduationAge,
62+
Math.max(firstGradeAge, Math.round(options.age || 16))
63+
)
64+
const text = toText(tree)
65+
const scores = readabilityScores(text)
66+
/** @type {number} */
67+
const readabilityAge =
68+
firstGradeAge +
69+
median([
70+
scores.daleChall,
71+
scores.ari,
72+
scores.colemanLiau,
73+
scores.fleschKincaid,
74+
scores.smog,
75+
scores.gunningFog
76+
])
77+
78+
// WPM the target audience normally reads.
79+
const targetWpm = baseWpm + (targetAge - firstGradeAge) * addedWpmPerGrade
80+
81+
// If the text requires higher comprehension than the target age group is,
82+
// estimated to have, make it a bit slower (and vice versa).
83+
const adjustedWpm =
84+
targetWpm - (readabilityAge - targetAge) * (addedWpmPerGrade / 2)
85+
86+
// Cap it to a WPM that’s reasonable.
87+
const wpm = Math.min(reasonableWpmMax, Math.max(baseWpm, adjustedWpm))
88+
89+
return Math.round((scores.wordCount / wpm) * accuracy) / accuracy
90+
}

license

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
(The MIT License)
2+
3+
Copyright (c) 2021 Titus Wormer <tituswormer@gmail.com>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
'Software'), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package.json

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"name": "hast-util-reading-time",
3+
"version": "0.0.0",
4+
"description": "hast utility to estimate the reading time",
5+
"license": "MIT",
6+
"keywords": [
7+
"unist",
8+
"hast",
9+
"hast-util",
10+
"util",
11+
"utility",
12+
"html",
13+
"reading",
14+
"time",
15+
"reading-time",
16+
"summary"
17+
],
18+
"repository": "syntax-tree/hast-util-reading-time",
19+
"bugs": "https://github.com/syntax-tree/hast-util-reading-time/issues",
20+
"funding": {
21+
"type": "opencollective",
22+
"url": "https://opencollective.com/unified"
23+
},
24+
"author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
25+
"contributors": [
26+
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)"
27+
],
28+
"sideEffects": false,
29+
"type": "module",
30+
"main": "index.js",
31+
"types": "index.d.ts",
32+
"files": [
33+
"index.d.ts",
34+
"index.js"
35+
],
36+
"dependencies": {
37+
"@types/hast": "^2.0.0",
38+
"compute-median": "^2.0.0",
39+
"hast-util-to-text": "^3.0.0",
40+
"readability-scores": "^1.0.0"
41+
},
42+
"devDependencies": {
43+
"@types/tape": "^4.0.0",
44+
"c8": "^7.0.0",
45+
"hastscript": "^7.0.0",
46+
"prettier": "^2.0.0",
47+
"remark-cli": "^10.0.0",
48+
"remark-preset-wooorm": "^9.0.0",
49+
"rimraf": "^3.0.0",
50+
"tape": "^5.0.0",
51+
"type-coverage": "^2.0.0",
52+
"typescript": "^4.0.0",
53+
"xo": "^0.44.0"
54+
},
55+
"scripts": {
56+
"prepack": "npm run build && npm run format",
57+
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
58+
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
59+
"test-api": "node test.js",
60+
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
61+
"test": "npm run build && npm run format && npm run test-coverage"
62+
},
63+
"prettier": {
64+
"tabWidth": 2,
65+
"useTabs": false,
66+
"singleQuote": true,
67+
"bracketSpacing": false,
68+
"semi": false,
69+
"trailingComma": "none"
70+
},
71+
"xo": {
72+
"prettier": true
73+
},
74+
"remarkConfig": {
75+
"plugins": [
76+
"preset-wooorm"
77+
]
78+
},
79+
"typeCoverage": {
80+
"atLeast": 100,
81+
"detail": true,
82+
"strict": true,
83+
"ignoreCatch": true
84+
}
85+
}

0 commit comments

Comments
 (0)