From 5397390be2bf211938e845d264634f8a52d326e1 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Wed, 22 Apr 2026 09:02:23 +0200 Subject: [PATCH] feat(06-01): add Nitro hook content:file:afterParse for reading-time injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Register `content:file:afterParse` hook to inject `wordCount` + `minutes` on every parsed markdown content object (D-19: 200 wpm, floor 1 min). - Import pure util `countWordsInMinimalBody` from app/utils/countWords. - Guard against non-`.md` files (defensive — hook fires on all sources). - Values persist in @nuxt/content SQLite DB and are queryable via queryCollection thanks to matching Zod fields (content.config.ts). --- .planning/STATE.md | 6 +++--- server/plugins/reading-time.ts | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 server/plugins/reading-time.ts diff --git a/.planning/STATE.md b/.planning/STATE.md index d39babb..8b49117 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,8 +3,8 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: Context gathered — ready for /gsd-plan-phase 6 -last_updated: "2026-04-21T23:11:57.514Z" -last_activity: 2026-04-21 +last_updated: "2026-04-22T06:55:05.535Z" +last_activity: 2026-04-22 progress: total_phases: 8 completed_phases: 3 @@ -26,7 +26,7 @@ progress: Phase: Phase 6 — Blog Pages Plan: — Status: Context gathered — ready for /gsd-plan-phase 6 -Last activity: 2026-04-21 +Last activity: 2026-04-22 Resume file: .planning/phases/06-blog-pages/06-UI-SPEC.md ## Accumulated Context diff --git a/server/plugins/reading-time.ts b/server/plugins/reading-time.ts new file mode 100644 index 0000000..1dea82e --- /dev/null +++ b/server/plugins/reading-time.ts @@ -0,0 +1,23 @@ +import { countWordsInMinimalBody } from '~/utils/countWords' + +/** + * Nitro plugin: compute reading time for every markdown content file at parse time. + * + * Injects `wordCount` (number) and `minutes` (number, min 1) on the content object. + * Values are persisted in the @nuxt/content SQLite DB and queryable via queryCollection + * thanks to the matching Zod schema fields in content.config.ts (per D-18 + D-19). + * + * Hook reference: https://content.nuxt.com/docs/advanced/hooks + */ +export default defineNitroPlugin((nitroApp) => { + nitroApp.hooks.hook('content:file:afterParse', (ctx) => { + const { file, content } = ctx + + // Only process markdown files (defensive — hook fires on all sources) + if (!file.id?.endsWith('.md')) return + + const wordCount = countWordsInMinimalBody(content.body) + content.wordCount = wordCount + content.minutes = Math.max(1, Math.ceil(wordCount / 200)) // D-19: 200 wpm, floor 1 min + }) +})