From 0404f2468e31679e769ef5524727a9fef622c180 Mon Sep 17 00:00:00 2001 From: Philippe Serhal Date: Fri, 10 Apr 2026 09:17:47 -0400 Subject: [PATCH] chore: shave ~1-2s off `pnpm test:types` `pnpm test:types` runs `nuxt prepare` before `vue-tsc`. In this repo, `nuxt prepare` does not just generate `.nuxt` files, it loads `nuxt.config.ts`, installs modules, and runs each module's `setup()`. The `blog` module's `setup()` calls `loadBlogPosts()`, which calls `fetchBlueskyAvatars()`, which hits the Bluesky API to fetch user avatars and writes those to disk. None of that is needed for type gen or type checking. The blog already gracefully handles missing avatars. We already had a pattern to conditionally skip expensive work during prepare. This uses the same pattern here. This does not break the actual blog build, because nuxt runs modules' `setup()` during build. --- modules/blog.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/blog.ts b/modules/blog.ts index 812deeb758..6c28c6de1e 100644 --- a/modules/blog.ts +++ b/modules/blog.ts @@ -87,7 +87,14 @@ function resolveAuthors(authors: Author[], avatarMap: Map): Reso * Returns all posts (including drafts) sorted by date descending. * Resolves Bluesky avatars at build time. */ -async function loadBlogPosts(blogDir: string, imagesDir: string): Promise { +async function loadBlogPosts( + blogDir: string, + options: { + imagesDir: string + resolveAvatars: boolean + }, +): Promise { + const { imagesDir, resolveAvatars } = options const files = await Array.fromAsync(glob(join(blogDir, '**/*.md').replace(/\\/g, '/'))) // First pass: extract raw frontmatter and collect all Bluesky handles @@ -120,8 +127,10 @@ async function loadBlogPosts(blogDir: string, imagesDir: string): Promise() // Second pass: validate with raw schema, then enrich authors with avatars const posts: BlogPostFrontmatter[] = [] @@ -150,13 +159,14 @@ export default defineNuxtModule({ const resolver = createResolver(import.meta.url) const blogDir = resolver.resolve('../app/pages/blog') const blogImagesDir = resolver.resolve('../public/blog/avatar') + const resolveAvatars = !nuxt.options._prepare nuxt.options.extensions.push('.md') nuxt.options.vite.vue = defu(nuxt.options.vite.vue, { include: [/\.vue($|\?)/, /\.(md|markdown)($|\?)/], }) - if (!existsSync(blogImagesDir)) { + if (resolveAvatars && !existsSync(blogImagesDir)) { await mkdir(blogImagesDir, { recursive: true }) } @@ -180,7 +190,10 @@ export default defineNuxtModule({ ) // Load posts once with resolved Bluesky avatars (shared across template + route rules) - const allPosts = await loadBlogPosts(blogDir, blogImagesDir) + const allPosts = await loadBlogPosts(blogDir, { + imagesDir: blogImagesDir, + resolveAvatars, + }) // Expose frontmatter for the `/blog` listing page. const showDrafts = nuxt.options.dev || !isProduction