Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
"mdast-util-to-string": "^4.0.0",
"prettier": "^3.5.3",
"prettier-plugin-astro": "^0.14.1",
"reading-time": "^1.5.0",
"remark-breaks": "^4.0.0",
"typescript": "^5.8.2",
"unist-util-filter": "^5.0.1"
Expand Down
41 changes: 18 additions & 23 deletions tools/remark-reading-time.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import getReadingTime from "reading-time";
import { toString } from "mdast-util-to-string";
import { filter } from "unist-util-filter";

import assert from "node:assert/strict";

const WORDS_PER_MINUTE = 265;

const segmenter = new Intl.Segmenter("en-US", {
granularity: "word",
});

// Food for thoughts: have 2 different metrics for images & code blocks.
// And use the image size as a baseline (bigger images will take more time than smaller ones),
// and same for code blocks: larger code blocks should take more time.
Expand Down Expand Up @@ -41,11 +44,11 @@ assert.equal(getSecondsAddedByImages(14), (totalForTest += 3));

export function remarkReadingTime() {
return (tree, { data }) => {
let nbOfImages = 0;
let imagesCount = 0;
const filteredTree = filter(tree, (node) => {
// Treat images & code as "meta content"
if (node.type === "image" || node.type === "code") {
nbOfImages++;
imagesCount++;
}
return (
node.type !== "code" && // Remove code blocks (easily read)
Expand All @@ -62,31 +65,23 @@ export function remarkReadingTime() {
// - https://help.medium.com/hc/en-us/articles/214991667-Read-time (for 265WPM)
// - https://mediumcourse.com/how-is-medium-article-read-time-calculated/ (for the image timing)

const readingTime = getReadingTime(textOnPage);

// `readingTime` contains a few information:
// - `text`: Human readable duration, like "3 min read",
// - `words`: The total word count
// - `minutes`: Total reading time duration in minutes
// - `time`: Total reading time duration in milliseconds
// As we want to manipulate this data manually, we'll just use `words`

const words = readingTime.words;
const wordsCount = [...segmenter.segment(textOnPage)].filter(
({ isWordLike }) => isWordLike,
).length;

let time = Math.round((words / WORDS_PER_MINUTE) * 60_000); // round to avoid issues with floats
let totalMs = Math.round((wordsCount / WORDS_PER_MINUTE) * 60_000); // round to avoid issues with floats

const nbOfImagesBellow10 = Math.min(nbOfImages, 10);
const timeAddedByImages = getSecondsAddedByImages(nbOfImages) * 1000; // to milliseconds
time += timeAddedByImages;
const nbOfImagesBellow10 = Math.min(imagesCount, 10);
const timeAddedByImages = getSecondsAddedByImages(imagesCount) * 1000; // to milliseconds
totalMs += timeAddedByImages;

const minutes = time / 60_000;
const displayedText = Math.ceil(minutes.toFixed(2)) + " min read";
const minutes = totalMs / 60_000;
const displayedText = Math.ceil(minutes) + " min read";

const finalReadingTime = {
words,
time,
minutes,
displayedText,
words: wordsCount, // The total word count
minutes, // Total reading time duration in minutes
displayedText, // Human readable duration, like "3 min read"
};

data.astro.frontmatter.readingTime = finalReadingTime;
Expand Down
8 changes: 0 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7788,7 +7788,6 @@ __metadata:
react-dom: "npm:^19.1.0"
react-dom-18: "patch:react-dom@npm%3A18.3.1#~/.yarn/patches/react-dom-npm-18.3.1-a805663f38.patch"
react-konva: "npm:^19.0.3"
reading-time: "npm:^1.5.0"
remark-breaks: "npm:^4.0.0"
reveal.js: "npm:^5.2.1"
typescript: "npm:^5.8.2"
Expand Down Expand Up @@ -8049,13 +8048,6 @@ __metadata:
languageName: node
linkType: hard

"reading-time@npm:^1.5.0":
version: 1.5.0
resolution: "reading-time@npm:1.5.0"
checksum: 10c0/0f730852fd4fb99e5f78c5b0cf36ab8c3fa15db96f87d9563843f6fd07a47864273ade539ebb184b785b728cde81a70283aa2d9b80cba5ca03b81868be03cabc
languageName: node
linkType: hard

"recma-build-jsx@npm:^1.0.0":
version: 1.0.0
resolution: "recma-build-jsx@npm:1.0.0"
Expand Down