# Chalarangelo/30-seconds-of-code — codebase unpack

> Canonical page: https://codevetter.com/unpack/30-seconds-of-code

This is a deterministic Repo Unpack scan of Chalarangelo/30-seconds-of-code — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: bd756f8b2283a38560efe83cb30af38324d4e9e9 (2026-09-09)
- Last upstream commit: Fix missing excerpt
- Generated by codevetter 1.12.0 on 2026-09-22
- 1113 files scanned · 5.8 MB

## Languages

- Markdown: 782 files
- JavaScript: 106 files
- YAML: 97 files
- Sass: 38 files
- JSON: 3 files
- TypeScript: 3 files
- CSS: 1 files

## Repository structure

- content/ — 881 files
- src/ — 132 files
- spec/ — 39 files
- public/ — 26 files
- .github/ — 7 files
- logo/ — 6 files
- bin/ — 3 files

Probable source entrypoint: `src/` (inferred from the tree, not upstream docs)

## Package

- package.json: 30-seconds-of-code v13.0.0 — 24 declared dependencies

## Structural health signals

Average score 9/10 across 96 files; 0 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- bd756f8b2283 (2026-09-09) Fix missing excerpt
- f1b2d5a65c32 (2026-08-08) Bump js-yaml from 4.3.0 to 4.3.1
- 53ad944d4e05 (2026-07-29) Bump postcss from 8.5.15 to 8.5.24
- 7187b05a88fa (2026-07-29) Revert "Bump sharp and astro"
- 390b7451ba93 (2026-07-20) Bump js-yaml from 4.2.0 to 4.3.0

## Overview

30-seconds-of-code is the source for the statically-generated website www.30secondsofcode.org, an Astro 6 site that publishes a large library of short-form coding articles ('snippets') organized into YAML-defined collections. It is distinctive in that almost everything is custom-built: a bespoke content pipeline (bin/prepare) parses Markdown front-matter and YAML into a .content/content.json intermediate, hydrates an in-process ActiveRecord-style model layer, serializes page payloads to JSON files that Astro routes consume via getStaticPaths, and ships a hand-rolled TF-IDF + trigram-fuzzy search engine that runs both at build time and in the browser. Deployment is a scheduled GitHub Actions job that simply curls a Cloudflare build hook.

## System map

The system is a two-phase static site generator: bin/prepare transforms content/ Markdown and YAML into .content/ JSON artifacts, then astro build renders pages whose getStaticPaths read those JSONs. A custom model layer (Model/RecordSet), page adapters, and serializers mediate between raw content and Astro routes. Client interactivity is a few vanilla JS scripts (omnisearch, hotkeys, toc) loaded via <script src> in Astro components — there are no framework islands. Hosting is static (Cloudflare build hook triggered from GitHub Actions), and tests run under Vitest against spec/ fixtures.
- Astro is configured with srcDir 'src/astro', site https://www.30secondsofcode.org, compressHTML, and inlineStylesheets 'always'. — [astro.config.mjs](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/astro.config.mjs) [src/astro/settings.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/settings.js)
- bin/prepare is the build orchestrator CLI with commands: full, dev, content, watch, assets, create, create-component, help. — [bin/prepare#L8-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L8-L30)
- npm scripts wire preparation into lifecycle hooks: predev/prebuild run bin/prepare (dev/full), dev/start run 'astro dev --port 8000', build runs 'astro build', test runs 'vitest', console runs bin/console. — [package.json#L43-57](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L43-L57)
- The project is pure ESM ('type': 'module'), requires Node >=22.14.0, and uses '#src/*' and '#spec/*' subpath imports mirrored in jsconfig paths. — [package.json#L8](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L8) [package.json#L35-42](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L35-L42) [package.json#L69-71](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L69-L71) [jsconfig.json](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/jsconfig.json)
- A custom in-process data layer exists: Model keeps static instances/indexedInstances/getterCache registries, caches getters via WeakMap redefinition, and exposes all/where/order/scope/find/search class APIs. — [src/core/model.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js)
- RecordSet extends Array with where (equality, array-membership, or predicate functions), order, pluck, shuffle, first/last — it is the query primitive used by every model scope. — [src/core/recordSet.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/recordSet.js)
- The model registry is exactly five classes: ContentModel (abstract base), Collection, CollectionSnippet (join model), Language, Snippet. — [src/models/models.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/models.js) [src/models/contentModel.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js)
- Loader.importData reads .content/content.json and hydrates model instances through a JSON.parse reviver keyed on each record's 'model' discriminator field. — [src/lib/loader.js#L43-54](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L43-L54) [src/models/models.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/models.js)
- Page adapters form a registry: Page.register stores classes by name and Page.from(object) resolves the adapter as `${object.constructor.name}Page`; PageSerializer emits {params, props} where props includes structuredData (schema.org JSON-LD). — [src/adapters/page.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js) [src/adapters/page/snippetPage.js#L4-8](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js#L4-L8) [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js)
- AstroContent.generate writes three JSON artifacts consumed by Astro routes: .content/pages/index.json, [lang]/[...listing].json, and [lang]/s/[snippet].json. — [src/lib/astroContent.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js) [src/config/settings.js#L43-48](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L43-L48)
- The search stack is hand-rolled and shared between build and browser: server.js tokenizes content into 'token:count' strings, DocumentIndex builds term + trigram inverted indexes, and documentSearch scores with TF-IDF blended with n-gram similarity. — [src/lib/search/server.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/server.js) [src/lib/search/documentIndex.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentIndex.js) [src/lib/search/documentSearch.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentSearch.js)
- Deployment is a scheduled GitHub Actions workflow (cron '20 18 * * *' plus workflow_dispatch) that executes bin/deploy, a bash script that POSTs to a Cloudflare build hook URL stored in the CF_BUILD_HOOK_URL secret. — [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [bin/deploy](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/deploy)
- Client-side behavior is delivered by three plain scripts referenced from components via <script src>: omnisearch.js (search modal), hotkeys.js (keyboard nav), toc.js (table of contents). — [src/astro/scripts/omnisearch.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js) [src/astro/scripts/hotkeys.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/hotkeys.js) [src/astro/scripts/toc.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/toc.js) [src/astro/components/Omnisearch.astro#L23](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/components/Omnisearch.astro#L23) [src/astro/components/Header.astro#L42](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/components/Header.astro#L42)
- Generated artifacts live in gitignored paths: .content/, src/astro/timestamp.js, public/search-data.json, public/sitemap.xml, public/feed.xml, public/_redirects, public/components. — [.gitignore](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.gitignore) [src/lib/timestampDump.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/timestampDump.js) [src/config/settings.js#L49-53](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L49-L53)

## Feature catalog

User-facing features are all static pages and client widgets: a home page, paginated collection listing pages, snippet article pages, an about page and 404, plus an omnisearch modal, keyboard shortcuts, RSS feed, sitemap, and redirects file. Content-level features (collections, journeys, article embeds, admonitions, codepen embeds, automatic code-reference links, table of contents) are produced by the custom markdown pipeline. Author-facing features include snippet/collection scaffolding commands, a watch mode, an interactive REPL console, and an asset pipeline that resizes cover images.
- Snippet pages at /{lang}/s/{snippet} are rendered by [snippet].astro with breadcrumb, snippet content, journey prev/next pagination, a 'More like this' recommendation list, a table-of-contents sidebar, and a content-components fallback. — [src/astro/pages/[lang]/s/[snippet].astro](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/s/%5Bsnippet%5D.astro) [src/adapters/page/snippetPage.js#L19-31](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js#L19-L31)
- Paginated listing pages are served by [lang]/[...listing].astro, which reads .content/pages/[lang]/[...listing].json and renders a Hero, optional sublink Chips, a PreviewList, and Pagination. — [src/astro/pages/[lang]/[...listing].astro](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/%5B...listing%5D.astro) [src/adapters/page/collectionPage.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/collectionPage.js)
- The home page composes a Hero, an optional Announcement banner (latest update-log snippet within the last 30 days), featured collection Chips (top 7 plus an 'Explore collections' link), and a PreviewList of the 12 newest published snippets. — [src/astro/pages/index.astro](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/index.astro) [src/adapters/page/homePage.js#L36-90](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/homePage.js#L36-L90) [src/config/settings.js#L25-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L25-L30)
- The omnisearch modal lazily fetches /search-data.json on first open, builds a DocumentIndex in the browser, searches on keyup (queries of 1 char or less are ignored), supports ArrowUp/ArrowDown result navigation, and closes on Escape, backdrop click, or the close trigger. — [src/astro/scripts/omnisearch.js#L17-35](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L17-L35) [src/astro/scripts/omnisearch.js#L72-76](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L72-L76) [src/astro/scripts/omnisearch.js#L152-201](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L152-L201)
- Keyboard hotkeys bind Ctrl/Cmd+k to omnisearch, Ctrl/Cmd+j to collections, and Ctrl/Cmd+h to home, with Mac detection via navigator.userAgent. — [src/astro/scripts/hotkeys.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/hotkeys.js)
- Collections are YAML-defined and populated three ways: explicit snippetIds lists, languageMatcher/tagMatcher filters, or the special 'snippets' main collection which ranks all snippets; 'collections' hub gets featured ordering from hub.yaml featuredListings. — [src/lib/contentUtils/modelWorkers/collectionSnippet.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/collectionSnippet.js) [src/lib/contentUtils/modelWorkers/collection.js#L45-48](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/collection.js#L45-L48) [content/collections/js/array.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/collections/js/array.yaml) [content/hub.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/hub.yaml)
- Journeys are multi-part articles: a snippet's journeyId front-matter links it to a Collection, and Snippet.journeyPagination computes previousUrl/nextUrl/url/title/itemNumber for the Pagination component. — [src/models/snippet.js#L99-132](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L99-L132) [src/lib/contentUtils/modelWorkers/snippet.js#L25](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L25)
- Article embeds: '<article-embed ref="..." title="..."/>' markup in rendered content is replaced at read time by Snippet.enrichedContent with an asEmbedding figure produced via ContentModel.searchContentModels. — [src/models/snippet.js#L67-83](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L67-L83) [src/models/contentModel.js#L21-32](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L21-L32) [src/models/contentModel.js#L151-171](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L151-L171) [src/lib/contentUtils/markdownParser/plugins/ast/transformArticleEmbeds.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/ast/transformArticleEmbeds.js)
- The markdown pipeline applies remark plugins (highlightCode, embedCodepensFromLinks, transformArticleEmbeds) then HAST plugins (loadWebComponents, linkInlineCode, safeguardExternalLinks, transformAdmonitions, transformHeadings for h2-h4 with linkable ids, transfomImagePaths, wrapTables) before rehype-stringify. — [src/lib/contentUtils/markdownParser/markdownParser.js#L80-95](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L80-L95) [src/lib/contentUtils/markdownParser/plugins/index.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/index.js)
- linkInlineCode auto-links inline code tokens to language reference URLs (e.g. MDN) declared in content/languages/*.yaml 'references' and 'additionalReferences' maps. — [src/lib/contentUtils/markdownParser/plugins/hast/linkInlineCode.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/hast/linkInlineCode.js) [content/languages/javascript.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/languages/javascript.yaml) [src/lib/contentUtils/markdownParser/markdownParser.js#L35-46](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L35-L46)
- SEO features: each Page emits schema.org JSON-LD (TechArticle for snippets, ItemList for collections, WebSite for home), Snippet.seoTitle prefixes the language name when absent from the title, and ContentModel.seoDescription strips HTML for meta descriptions. — [src/adapters/page.js#L40-53](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L40-L53) [src/adapters/page/snippetPage.js#L33-47](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js#L33-L47) [src/models/snippet.js#L138-149](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L138-L149) [src/models/contentModel.js#L71-77](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L71-L77)
- Build-time outputs for the site include an RSS feed (public/feed.xml), a sitemap listing home/about/snippets/all collection pages, and a Netlify-style _redirects file generated from content/redirects.yaml. — [src/lib/feed.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/feed.js) [src/lib/sitemap.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/sitemap.js) [src/lib/redirects.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/redirects.js) [content/redirects.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/redirects.yaml)
- The asset pipeline (prepareAssets) uses sharp to convert content/assets/{cover,splash} images into sized .webp variants in public/assets — covers at 800x400/400x200/1200x600 and splashes at 600x600/400x400 — and copies static 'icons' and 'illustrations' directories. — [src/lib/contentUtils/assetHandler.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/assetHandler.js) [src/config/settings.js#L84-99](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L84-L99)
- Content web components are supported: scripts under content/components/scripts are copied and SCSS under content/components/styles is compiled to public/components; the loadWebComponents HAST plugin injects <script type=module> tags when markup references them. — [src/lib/contentComponents.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentComponents.js) [src/lib/contentUtils/markdownParser/plugins/hast/loadWebComponents.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/hast/loadWebComponents.js) [src/lib/contentUtils/config.js#L12-14](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js#L12-L14)
- Authoring commands: 'bin/prepare create [snippet|collection] [dir] [name]' scaffolds content from content/collection-template.yaml or per-directory snippet-template.md files, filling title/slug/dateModified; 'bin/prepare create-component' scaffolds components; 'bin/console' opens a REPL with all models and PreparedQueries preloaded. — [src/lib/contentUtils/contentCreator.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/contentCreator.js) [content/collection-template.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/collection-template.yaml) [content/snippets/js/snippet-template.md](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/snippets/js/snippet-template.md) [bin/console](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/console) [src/lib/preparedQueries.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/preparedQueries.js)

## Data flow

Data flows one direction: content/ sources → extraction (FileHandler globs + front-matter/YAML parsing) → per-model workers that render HTML, tokenize, rank, and build join records → .content/content.json → in-memory model graph via Loader reviver → page adapters + serializers → .content/pages/*.json plus public/ artifacts (search-data.json, feed.xml, sitemap.xml, _redirects) → Astro getStaticPaths → static HTML. The browser is a terminal consumer: it fetches search-data.json and rebuilds the same DocumentIndex used at build time. There is no runtime server state; 'state owners' are the static Model registries hydrated once per build/console process.
- Input boundaries are globbed files: content/snippets/**/s/*.md (front-matter + markdown), content/collections/**/*.yaml, content/languages/*.yaml, plus singleton files grammars.yaml, rankingEngine.yaml, hub.yaml — all read through FileHandler.read which dispatches on extension to JSON/YAML/front-matter readers. — [src/lib/contentUtils/config.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js) [src/lib/contentUtils/fileHandler.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/fileHandler.js)
- extractData is the single orchestration point: it loads languages, grammars, hub, and keyword data; selects Shiki or Prism as codeHighlighter; calls MarkdownParser.setupProcessors and sets Ranker.keywordScores; then extracts snippets, collections, and collectionSnippets in that order. — [src/lib/contentUtils/extractor.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/extractor.js)
- Snippet extraction derives id from filePath by stripping the 'content/snippets/' prefix and '.md' suffix, renders excerpt and body to HTML through a per-language MarkdownParser processor, computes recTokens and docTokens via tokenize(), computes a 0-1 ranking via Ranker.rankIndexableContent, and builds tableOfContentsHtml via TocReader when tocEnabled. — [src/lib/contentUtils/modelWorkers/snippet.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js) [src/lib/contentUtils/ranker.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/ranker.js) [src/lib/contentUtils/tocReader.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/tocReader.js)
- MarkdownParser maintains one unified() processor per language short-code plus a 'default'; each processor embeds that language's reference map (merged from allLanguageReferences) for linkInlineCode, so inline-code autolinking is per-language. — [src/lib/contentUtils/markdownParser/markdownParser.js#L29-99](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L29-L99)
- exportXData functions flatten extracted objects into plain records tagged with a 'model' field ('Snippet', 'Collection', 'Language', 'CollectionSnippet'); ContentUtils.prepareContent writes the aggregate to .content/content.json via FileHandler.write. — [src/lib/contentUtils/contentUtils.js#L14-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/contentUtils.js#L14-L26) [src/lib/contentUtils/modelWorkers/snippet.js#L98-119](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L98-L119)
- CollectionSnippet join records are produced by extractCollectionSnippetData: explicit snippetIds are looked up with a console.warn on missing, matcher-based collections filter ranked snippets by languageKey/tags, the 'snippets' main collection gets position = rank index (-1 for unlisted), and allowUnlisted controls inclusion. — [src/lib/contentUtils/modelWorkers/collectionSnippet.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/collectionSnippet.js)
- On load, every model constructor pushes itself into Model.instances[name] and registers each declared index into Model.indexedInstances[name][index] maps; all Snippet and Collection instances are indexed on 'id'. — [src/core/model.js#L52-63](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L52-L63) [src/models/snippet.js#L13-16](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L13-L16) [src/models/collection.js#L10-13](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/collection.js#L10-L13)
- Page serialization path: AstroContent.generate calls Collection.pages / snippet.page / Page.home, each Page subclass exposes params and props getters, PageSerializer.serialize emits {params, props:{...props, structuredData: schemaData}}, and results are keyed by page.key into JSON files under .content/pages/. — [src/lib/astroContent.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js) [src/adapters/page.js#L10-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L10-L30) [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js) [src/adapters/page/collectionPage.js#L9-29](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/collectionPage.js#L9-L29)
- Astro routes are pure consumers of generated JSON: [snippet].astro and [...listing].astro implement getStaticPaths by reading the corresponding .content JSON file and mapping entries to {params, props}; index.astro reads index.json synchronously at module scope. — [src/astro/pages/[lang]/s/[snippet].astro#L15-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/s/%5Bsnippet%5D.astro#L15-L26) [src/astro/pages/[lang]/[...listing].astro#L11-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/%5B...listing%5D.astro#L11-L26) [src/astro/pages/index.astro#L13-14](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/index.astro#L13-L14)
- Search data path: snippet/collection docTokens strings ('token:count' space-joined) are serialized by SearchResultSerializer into public/search-data.json; the browser fetches it, deserializeTokens rebuilds Map<term,freq>, and DocumentIndex re-derives trigram sets for fuzzy matching. — [src/lib/searchIndex.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/searchIndex.js) [src/serializers/searchResultSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/searchResultSerializer.js) [src/astro/scripts/omnisearch.js#L19-34](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L19-L34) [src/lib/search/utils.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/utils.js)
- Ranker scores content against the sorted keyword table from content/rankingEngine.yaml, capping at 20 keywords or a score of 100, and returns max(0.0001, score/100) so no snippet ranks exactly 0. — [src/lib/contentUtils/ranker.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/ranker.js) [content/rankingEngine.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/rankingEngine.yaml)
- Redirects flow: content/redirects.yaml is loaded once in a static block, Redirects.generate writes 'from to status' lines to public/_redirects, and Redirects.for(slug) does a transitive lookup used by ContentModel.allSlugs for canonical/alias handling. — [src/lib/redirects.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/redirects.js) [src/models/contentModel.js#L58-60](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L58-L60) [content/redirects.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/redirects.yaml)
- TimestampDump.generate writes a hex timestamp to src/astro/timestamp.js, which Layout.astro injects via define:vars into an empty script solely to force HMR when content regenerates during watch mode. — [src/lib/timestampDump.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/timestampDump.js) [src/astro/layouts/Layout.astro#L9-34](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/layouts/Layout.astro#L9-L34)

## Behavior traces

The three flows worth knowing end-to-end: (1) 'npm run dev' runs the content pipeline with Prism fast-highlighting then starts astro dev; (2) 'npm run build' runs the full pipeline (assets + Shiki + all generated artifacts) then astro build; (3) at runtime, the only dynamic behavior is the omnisearch modal which fetches the prebuilt search index. A fourth flow — watch mode — respawns the whole dev pipeline per file change, and deployment is a cron-triggered Cloudflare webhook call.
- Dev startup trace: npm run dev → predev 'bin/prepare dev --fast-highlight' → sets NODE_ENV=development → ContentUtils.prepareContent('prism') → extractData → FileHandler.write(.content/content.json) → Loader.loadModules + importData → AstroContent.generate → SearchIndex.generate → TimestampDump.generate → ContentComponents.prepare → 'astro dev --port 8000'. — [bin/prepare#L66-90](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L66-L90) [package.json#L44-45](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L44-L45) [src/lib/contentUtils/contentUtils.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/contentUtils.js)
- Full build trace: npm run build → prebuild 'bin/prepare full' → ContentUtils.prepareAssets (sharp image conversion, non-forced) → prepareContent with Shiki (fastHighlight=false) → importData → AstroContent.generate → SearchIndex.generate → Redirects.generate → Feed.generate → Sitemap.generate → TimestampDump.generate → ContentComponents.prepare → 'astro build'. — [bin/prepare#L29-65](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L29-L65) [package.json#L50-51](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L50-L51)
- Watch trace: 'bin/prepare watch [--fast-highlight]' → FileWatcher.watch does fs.watch('content', {recursive:true}) → every event logs the filename and exec()s a fresh 'bin/prepare dev' child process — a full re-extraction per change with no debounce. — [bin/prepare#L91-99](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L91-L99) [src/lib/contentUtils/fileWatcher.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/fileWatcher.js)
- Snippet page render trace (at astro build): getStaticPaths reads .content/pages/[lang]/s/[snippet].json → each serialized SnippetPage entry supplies params {lang, snippet} and props {breadcrumbs, pageDescription, snippet(context), journey(pagination), recommendations(collection+snippets, ≤4 previews)} → Layout renders Breadcrumb, SnippetContent, Pagination (if journey), PreviewList, ContentComponentsFallback, TableOfContents. — [src/astro/pages/[lang]/s/[snippet].astro](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/s/%5Bsnippet%5D.astro) [src/adapters/page/snippetPage.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js) [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js)
- Recommendation trace inside a snippet page build: SnippetPage.props reads object.recommendedSnippets → RecommendationPresenter.recommendSnippets() scores candidates pre-grouped by languageId and primaryTag (language up to 45 pts, primary tag up to 15, recToken overlap up to 40, thresholds per settings.recommendations) → top results merged with recommendedCollection and sliced to 4 previews. — [src/presenters/recommendationPresenter.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/presenters/recommendationPresenter.js) [src/config/settings.js#L100-121](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L100-L121) [src/adapters/page/snippetPage.js#L24-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js#L24-L30)
- Omnisearch runtime trace: user clicks [data-open-modal] → open() → prepare() fetches /search-data.json once → documents mapped {id:url, content:deserializeTokens(searchTokens)} → new DocumentIndex → search(index) closure → keyup on #omnisearch → searchByKeyphrase trims/lowercases, returns [] for len<=1 → searchDocuments(q,100) → TF-IDF term scoring + 0.7-weighted ngram similarity → displayResults writes innerHTML list. — [src/astro/scripts/omnisearch.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js) [src/lib/search/documentSearch.js#L109-149](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentSearch.js#L109-L149) [src/lib/search/documentIndex.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentIndex.js)
- Scheduled publishing trace: snippet front-matter dateModified in the future → Snippet.isScheduled true → Snippet.scope('published') excludes it; AstroContent.generateSnippetPages uses Snippet.all when NODE_ENV==='development' but Snippet.scope('published') otherwise, so scheduled articles appear locally but not in production builds. — [src/lib/astroContent.js#L45-54](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js#L45-L54) [src/models/snippet.js#L47-55](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L47-L55) [src/models/snippet.js#L180-190](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L180-L190)
- Deploy trace: deploy-production.yml runs on cron '20 18 * * *' or manual dispatch → checkout@v1 → chmod +x ./bin/deploy → './bin/deploy production' → bash curl -X POST to $DEPLOY_URL (secret CF_BUILD_HOOK_URL) with trigger_title and trigger_branch=master → Cloudflare rebuilds and serves the static site. — [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [bin/deploy](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/deploy)
- Content creation trace: 'npm run create' → 'bin/prepare create snippet js my-name' → ContentCreator.create resolves content/snippets/js/snippet-template.md as the template → replaces title and dateModified (today) via regex → mkdirs content/snippets/js/s if needed → writes my-name.md; collections similarly use content/collection-template.yaml with slug/name replacement. — [package.json#L53](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L53) [bin/prepare#L105-112](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L105-L112) [src/lib/contentUtils/contentCreator.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/contentCreator.js)
- Console trace: 'npm run console' → bin/console starts node:repl → Loader.loadModules + importData hydrates the full model graph → models/serializers/pages/module classes injected into REPL context → PreparedQueries functions bound and documentIndex prebuilt → exposes helpers like searchForTerm, snippetsWithoutFormatting, snippetCoverUsage. — [bin/console](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/console) [src/lib/preparedQueries.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/preparedQueries.js)

## Testing signals

Tests are Vitest specs under spec/, mirroring src/ structure: core, models, presenters, and lib/contentUtils. Coverage is real but selective — model logic, presenters, ranker, tocReader, stringUtils, search utils, metaParser, and an integration-style prepareContent test against fixture content. Factories (base + named traits) are the fixture mechanism, built through Loader.buildFactories. CI runs 'npm ci && npm test' on Node 22.14 for pushes and PRs to master. Notable gaps: no specs for serializers, page adapters, documentIndex/documentSearch/porterStemmer, assetHandler, extractor end-to-end, or any Astro/client code.
- 'npm test' runs bare 'vitest' (no config file found at repo root); CI invokes the same command on Node 22.14.0 for push/PR to master. — [package.json#L56](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L56) [.github/workflows/test.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/test.yml)
- Spec layout mirrors src/: spec/core (model, serializer), spec/models (collection, contentModel, snippet), spec/presenters (breadcrumb, cover, recommendation, sublink), spec/lib (contentUtils, ranker, tocReader, stringUtils, search/utils, metaParser). — [spec/core/model.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/core/model.test.js) [spec/models/snippet.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/models/snippet.test.js) [spec/presenters/recommendationPresenter.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/presenters/recommendationPresenter.test.js) [spec/lib/contentUtils/ranker.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/lib/contentUtils/ranker.test.js) [spec/lib/search/utils.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/lib/search/utils.test.js)
- The fixture mechanism is factory objects: spec/factories export {model, base, traits} and Loader.buildFactories() composes base + named traits + inline overrides, then news up the registered model class. — [spec/factories/factories.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/factories/factories.js) [spec/factories/snippetFactory.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/factories/snippetFactory.js) [src/lib/loader.js#L56-76](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L56-L76)
- spec/lib/contentUtils/contentUtils.test.js is an integration test: it vi.mocks the config module to point at spec/fixtures/content and FileHandler.write to capture output, then runs the real ContentUtils.prepareContent and asserts output keys and language objects. — [spec/lib/contentUtils/contentUtils.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/lib/contentUtils/contentUtils.test.js) [spec/mocks/lib/contentUtils/config.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/mocks/lib/contentUtils/config.js)
- spec/fixtures/content is a miniature of the real content tree — snippets (js/css/articles), collections (js/css/webdev), languages (js/css/html), grammars.yaml, hub.yaml, rankingEngine.yaml — letting the pipeline run end-to-end without touching real content. — [spec/fixtures/content/snippets/js/s/array-map-foreach.md](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/fixtures/content/snippets/js/s/array-map-foreach.md) [spec/fixtures/content/collections/js/array.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/fixtures/content/collections/js/array.yaml) [spec/fixtures/content/rankingEngine.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/fixtures/content/rankingEngine.yaml)
- snippet.test.js demonstrates the shared-global-state pattern: Loader.loadModules() and Factory.create() calls run at describe scope, registering instances into the process-global Model.instances registry — meaning tests are implicitly order/isolation sensitive. (inferred) — [spec/models/snippet.test.js#L5-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/models/snippet.test.js#L5-L30) [src/core/model.js#L4-7](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L4-L7)
- Coverage gaps: no spec exists for serializers/*, adapters/*, lib/search/documentIndex.js, documentSearch.js, porterStemmer.js, extractor.js, assetHandler.js, astroContent.js, or any src/astro code — the .content/pages JSON contract and the browser search index have no automated tests. — [spec/lib/search/utils.test.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/lib/search/utils.test.js) [src/lib/search/documentIndex.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentIndex.js) [src/adapters/page.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js) [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js)
- Linting is eslint 9 flat config (js.configs.recommended + eslint-config-prettier) with a custom globals.js file; style rules enforce single quotes, semicolons, multiline comma-dangle, as-needed arrow-parens; prettier + prettier-plugin-astro via .prettierrc. No lint or format check runs in CI. — [eslint.config.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/eslint.config.js) [globals.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/globals.js) [.prettierrc](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.prettierrc) [.github/workflows/test.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/test.yml)
- Other workflows are maintenance automation only: stale.yml marks inactive issues/PRs via actions/stale@v5 and label.yml applies path-based PR labels via actions/labeler@v4 — neither runs tests or builds. — [.github/workflows/stale.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/stale.yml) [.github/workflows/label.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/label.yml)
- There is no coverage tooling, no e2e/browser test, and no build-verification step in CI — the 'test' job is the only quality gate and it never runs 'astro build' or 'bin/prepare full'. — [.github/workflows/test.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/test.yml) [package.json](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json)

## Risk map

Top risks: production code imports test factories (spec/ is on the live path of bin/console and any Loader user); the model layer is global mutable state; the markdown pipeline passes through raw HTML (allowDangerousHtml) so content is trusted input; watch mode has no debounce; omnisearch injects index data into innerHTML; and several identifiers are derived by brittle string slicing/naming conventions. Deploy security hinges on a single secret webhook URL, and CI is thin (tests only, old action versions, no build check).
- src/lib/loader.js imports '#spec/factories/factories.js' — test-only factory code is imported by a production module, coupling any Loader user (bin/prepare, bin/console) to the spec tree. — [src/lib/loader.js#L18](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L18) [spec/factories/factories.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/factories/factories.js)
- Model state is process-global and append-only: Model.instances accumulates every constructed instance with no reset API, so double importData calls or tests that create factories leak records into all/where/scope results for the process lifetime. — [src/core/model.js#L4-7](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L4-L7) [src/core/model.js#L52-63](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L52-L63) [src/lib/loader.js#L43-54](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L43-L54)
- The markdown pipeline runs remark-rehype and rehype-stringify with allowDangerousHtml:true — raw HTML embedded in snippet markdown passes through unescaped into shipped pages; safe only because all content is author-trusted. — [src/lib/contentUtils/markdownParser/markdownParser.js#L86](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L86) [src/lib/contentUtils/markdownParser/markdownParser.js#L95](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L95)
- omnisearch renders result rows via innerHTML template literals interpolating result.url, result.title, result.tag without escaping (createResultHTML), while only the not-found path escapes the query — a crafted search-data.json title could inject markup, though the index is build-generated. — [src/astro/scripts/omnisearch.js#L94-117](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/scripts/omnisearch.js#L94-L117)
- Watch mode exec()s a full 'bin/prepare dev' on every fs.watch event with no debounce or in-flight guard — rapid saves can spawn overlapping full re-extractions (Shiki highlighting is the expensive step). — [bin/prepare#L91-99](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L91-L99) [src/lib/contentUtils/fileWatcher.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/fileWatcher.js)
- Snippet id derivation is positional: filePath.replace(snippetPrefix,'').slice(0,-3) assumes the 'content/snippets/…/*.md' shape; a stray file matched by the glob with a different extension would silently corrupt the id. — [src/lib/contentUtils/modelWorkers/snippet.js#L29](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L29) [src/lib/contentUtils/config.js#L6](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js#L6)
- A missing/typo'd front-matter 'language' degrades silently: languageData.get(languageKey) returns undefined and every downstream use is optional-chained, so recTokens/docTokens lose language terms and the snippet renders without a language rather than failing. — [src/lib/contentUtils/modelWorkers/snippet.js#L28-46](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L28-L46)
- Naming-convention coupling is load-bearing: Page.from resolves `${object.constructor.name}Page`, serializeAs resolves `${name}Serializer`, and the JSON reviver resolves models[value.model] — renaming a class or key breaks page generation, and the 'serializer not found' guard in serializeAs is dead code because `new undefined()` throws TypeError first. — [src/adapters/page.js#L10-13](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L10-L13) [src/models/contentModel.js#L118-127](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L118-L127) [src/lib/loader.js#L47-50](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L47-L50)
- Publishing correctness depends on wall-clock time: dateModified compared to new Date() decides scheduled vs published, so the daily 18:20 UTC deploy cron determines when scheduled articles go live — a missed/failed deploy silently delays publication. (inferred) — [src/models/snippet.js#L180-190](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L180-L190) [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [src/lib/astroContent.js#L45-49](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js#L45-L49)
- Astro routes hard-fail without generated content: getStaticPaths does fs.readFile on .content JSONs with no fallback, so 'astro dev'/'astro build' without the prepare step (or a cleaned .content dir) crashes — the coupling is enforced only by npm pre-hooks, which bare 'astro' or 'vitest' invocations bypass. — [src/astro/pages/[lang]/s/[snippet].astro#L15-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/%5Blang%5D/s/%5Bsnippet%5D.astro#L15-L26) [src/astro/pages/index.astro#L13-14](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/index.astro#L13-L14) [package.json#L44-51](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L44-L51)
- Known-fragile code is flagged in TODO comments: Snippet.hasCollection returns true even when the only membership is the main collection, and Collection.matchesTag relies on id.endsWith(`/${tag}`). — [src/models/snippet.js#L212-217](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L212-L217) [src/models/collection.js#L160-163](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/collection.js#L160-L163)
- CI uses dated action versions (actions/checkout@v1 in deploy-production, actions/setup-node@v1 in test) and the deploy job's only secret is CF_BUILD_HOOK_URL — a leaked or misconfigured hook URL is the entire deployment control surface. — [.github/workflows/deploy-production.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/deploy-production.yml) [.github/workflows/test.yml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.github/workflows/test.yml) [bin/deploy](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/deploy)
- Shiki vs Prism output differs subtly: Prism mode appends a '<script type=module …/prism-code-highlights.mjs>' tag into parsed HTML, meaning dev-mode rendered markup is not identical to production markup. — [src/lib/contentUtils/markdownParser/markdownParser.js#L113-118](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L113-L118) [bin/prepare#L69-74](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L69-L74)

## Extension points

The repo is designed for extension mostly at the content layer: drop a markdown file into content/snippets/{lang}/s/, a YAML into content/collections or content/languages, and the glob-driven pipeline picks it up automatically. Code-level extension follows consistent registries and naming conventions: new models register via Model.prepare + models.js, new page types via Page.register + `${Model}Page`, new serializers via Serializer.prepare + `${name}Serializer`, and new markdown features via the remark/HAST plugin list in plugins/index.js. Search, ranking, and presentation are tuned through data files and settings rather than code.
- New snippet: add {name}.md under content/snippets/{lang}/s/ with front-matter (title, shortTitle, language, tags, cover, excerpt, listed, dateModified, optional tocEnabled/journeyId); the content/snippets/**/s/*.md glob picks it up with zero code changes. — [src/lib/contentUtils/config.js#L6](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js#L6) [src/lib/contentUtils/modelWorkers/snippet.js#L13-26](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/snippet.js#L13-L26) [content/snippets/js/s/resolve-promise-after-amount-of-time.md](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/snippets/js/s/resolve-promise-after-amount-of-time.md)
- New collection: add a YAML under content/collections with slug, title, description, and either explicit snippetIds or languageMatcher/tagMatcher; optional parent/topLevel/listed/allowUnlisted control hierarchy and visibility; hub.yaml featuredListings controls homepage featuring order. — [content/collections/js/array.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/collections/js/array.yaml) [src/lib/contentUtils/modelWorkers/collection.js#L11-24](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/collection.js#L11-L24) [content/hub.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/hub.yaml)
- New language: add {name}.yaml under content/languages with short/long/name/references/additionalReferences — it is globbed automatically and its references map feeds linkInlineCode autolinking and merged additionalReferences. — [src/lib/contentUtils/modelWorkers/language.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/modelWorkers/language.js) [src/lib/contentUtils/config.js#L7](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/config.js#L7) [content/languages/javascript.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/languages/javascript.yaml)
- New model: extend ContentModel (or Model), add a static block calling Model.prepare(this, [indexes]) and ContentModel.addContentModel(this), define scopes as static methods taking a records param, and register it in models.js so the JSON reviver can instantiate it. — [src/models/snippet.js#L13-16](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L13-L16) [src/models/contentModel.js#L16-19](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L16-L19) [src/models/models.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/models.js) [src/lib/loader.js#L47-50](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L47-L50)
- New page type: create a Page subclass with a static { Page.register(this) } block, implement params and props getters, optionally additionalSchemaData — PageSerializer picks it up automatically; the class MUST be named `${Model}Page` for Page.from resolution. — [src/adapters/page.js#L6-13](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L6-L13) [src/adapters/page/snippetPage.js#L4-8](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page/snippetPage.js#L4-L8) [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js)
- New serializer: extend Serializer, declare fields via Serializer.prepare(this, [key | [key, fn], ...]), and expose it through serializers.js — ContentModel.serializeAs resolves `${name}Serializer` by name. — [src/serializers/pageSerializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/pageSerializer.js) [src/serializers/serializers.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/serializers/serializers.js) [src/models/contentModel.js#L118-127](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L118-L127) [src/core/serializer.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/serializer.js)
- New markdown transform: add a plugin under plugins/ast (pre-remarkRehype, operates on mdast) or plugins/hast (post-remarkRehype, operates on hast), export it from plugins/index.js, and .use() it in MarkdownParser.setupProcessors — ordering constraints are documented inline (e.g. linkInlineCode must precede safeguardExternalLinks). — [src/lib/contentUtils/markdownParser/markdownParser.js#L52-95](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L52-L95) [src/lib/contentUtils/markdownParser/plugins/index.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/index.js)
- Highlighter swap/extension: ShikiHighlighter and PrismHighlighter are interchangeable via the 'shiki'|'prism' parameter selected by the --fast-highlight flag; Shiki languages are driven by content/grammars.yaml and the theme by codeHighlighters/themes/cosmos.json; custom Shiki transformers live in codeHighlighters/transformers. — [src/lib/contentUtils/extractor.js#L25-27](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/extractor.js#L25-L27) [src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js) [src/lib/contentUtils/markdownParser/codeHighlighters/prism.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/codeHighlighters/prism.js) [content/grammars.yaml](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/content/grammars.yaml)
- Search tuning is config-driven: stop words live in src/lib/search/settings.js, token filtering rules in server.js tokenFilter, fuzzy weight is a parameter to search(), and recommendation scoring constants are in settings.js recommendations block. — [src/lib/search/settings.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/settings.js) [src/lib/search/server.js#L25-40](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/server.js#L25-L40) [src/lib/search/documentSearch.js#L111](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/search/documentSearch.js#L111) [src/config/settings.js#L100-121](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L100-L121)
- Presentation is tuned in settings.js: cardsPerPage/collectionCardsPerPage, home card counts, tag display-name map, cover sizes/suffixes/srcsets, sublink labels, and collection id constants (mainCollectionId, updateLogsCollectionId, updateLogTag). — [src/config/settings.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js)
- New generated artifact: write a class with a static generate() writing into settings.paths.out.* and call it from bin/prepare's full/dev command lists — Redirects, Feed, Sitemap, TimestampDump, SearchIndex, AstroContent all follow this pattern. — [bin/prepare#L52-65](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L52-L65) [src/lib/redirects.js#L14-22](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/redirects.js#L14-L22) [src/lib/timestampDump.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/timestampDump.js)
- New client behavior: add a plain JS file under src/astro/scripts and reference it from an .astro component via <script src='../scripts/x.js'>; for reusable content widgets, drop scripts/styles under content/components and let loadWebComponents inject them into rendered HTML. — [src/astro/components/Omnisearch.astro#L23](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/components/Omnisearch.astro#L23) [src/lib/contentComponents.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentComponents.js) [src/lib/contentUtils/markdownParser/plugins/hast/loadWebComponents.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/plugins/hast/loadWebComponents.js)

## Contributor handoff

Conventions: ESM everywhere, #src/#spec import aliases, single quotes + semicolons + multiline trailing commas, class-static utility style, and ActiveRecord-ish model scopes. Safe edit boundary is content/ for content work (CONTRIBUTING restricts PRs to content/snippets and content/collections) and src/ for pipeline code — never edit generated paths (.content/, public/search-data.json, src/astro/timestamp.js, etc. — all gitignored). Before touching rendering, read settings.js, contentUtils/config.js, extractor.js, the modelWorkers, and the relevant model+adapter pair. Run 'npm test' (Vitest) after changes; use Loader.buildFactories in specs. Traps: global model registry, class-name coupling, scheduled-vs-published filtering differences between dev and prod, and the mandatory prepare step before any astro command.
- Always run the prepare step before astro commands: 'npm run dev'/'npm run build' chain it via pre-hooks, but a bare 'astro dev', 'astro build', or editor-driven vitest run against a missing .content/ will crash — .content is gitignored so fresh clones must generate it. — [package.json#L44-51](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L44-L51) [.gitignore](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.gitignore) [src/astro/pages/index.astro#L13-14](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/astro/pages/index.astro#L13-L14)
- Import style: use the '#src/…' and '#spec/…' aliases declared in package.json 'imports' (mirrored in jsconfig.json paths); the codebase is 'type': 'module' and requires Node >=22.14.0. — [package.json#L35-42](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L35-L42) [package.json#L69-71](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/package.json#L69-L71) [jsconfig.json](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/jsconfig.json)
- Code style is enforced by eslint flat config: single quotes (avoidEscape ok), semicolons, always-multiline comma-dangle (functions never), as-needed arrow-parens, 1tbs braces, plus prettier with prettier-plugin-astro — match surrounding style since CI does not lint. — [eslint.config.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/eslint.config.js) [.prettierrc](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.prettierrc) [globals.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/globals.js)
- Recommended read order for pipeline work: src/config/settings.js → src/lib/contentUtils/config.js → extractor.js → modelWorkers/* → src/models/* → src/adapters/page* → src/serializers/* → src/astro/pages/*. (inferred) — [src/config/settings.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js) [src/lib/contentUtils/extractor.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/extractor.js) [src/models/models.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/models.js) [src/adapters/page.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js)
- In specs, construct records via Loader.loadModules() + Loader.buildFactories() with named traits (e.g. SnippetFactory.create('published', {id: 'x'})) — never new models directly without understanding that each construction registers globally in Model.instances. — [spec/models/snippet.test.js#L5-30](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/spec/models/snippet.test.js#L5-L30) [src/lib/loader.js#L56-76](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L56-L76) [src/core/model.js#L55](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/core/model.js#L55)
- Do not rename model classes, page adapters, or serializers: Page.from uses object.constructor.name, the content.json reviver uses the 'model' string field, and serializeAs concatenates names — renaming requires updating all three conventions at once. — [src/adapters/page.js#L10-13](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/adapters/page.js#L10-L13) [src/lib/loader.js#L47-50](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/loader.js#L47-L50) [src/models/contentModel.js#L118-127](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/contentModel.js#L118-L127)
- Dev/prod asymmetry: NODE_ENV=development includes scheduled (future dateModified) snippets in generated pages; production excludes them — verify dateModified front-matter when a snippet 'disappears' after deploy. — [src/lib/astroContent.js#L45-49](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/astroContent.js#L45-L49) [src/models/snippet.js#L180-186](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/models/snippet.js#L180-L186)
- CONTRIBUTING ground rules restrict content PRs to content/snippets and content/collections; content should follow the writing guidelines (US spelling, short sentences, Grade-10 level, imperative code docs) in CONTRIBUTING.md. — [CONTRIBUTING.md](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/CONTRIBUTING.md)
- Use 'bin/prepare dev --fast-highlight' (Prism) for local iteration — Shiki highlighting is the slow path reserved for full builds; remember Prism mode appends a script tag to rendered HTML so markup differs slightly between modes. — [bin/prepare#L69-74](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/prepare#L69-L74) [src/lib/contentUtils/markdownParser/markdownParser.js#L113-118](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L113-L118)
- Generated outputs to never hand-edit: .content/**, src/astro/timestamp.js, public/search-data.json, public/sitemap.xml, public/feed.xml, public/_redirects, public/components, public/assets — all produced by bin/prepare and listed in .gitignore. — [.gitignore](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/.gitignore) [src/config/settings.js#L43-54](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/config/settings.js#L43-L54)
- Debugging/exploration tool: 'npm run console' opens a REPL with all models, serializers, pages, PreparedQueries functions, and a prebuilt documentIndex — the fastest way to inspect the hydrated content graph. — [bin/console](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/bin/console) [src/lib/preparedQueries.js](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/preparedQueries.js)
- When editing the markdown pipeline, respect documented plugin ordering: highlightCode, embedCodepensFromLinks, transformArticleEmbeds must run before remarkRehype (mdast phase); linkInlineCode must run before safeguardExternalLinks; transformHeadings handles only h2-h4 and adds the linkable ids TocReader later matches with a regex. — [src/lib/contentUtils/markdownParser/markdownParser.js#L52-95](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/markdownParser/markdownParser.js#L52-L95) [src/lib/contentUtils/tocReader.js#L5-7](https://github.com/Chalarangelo/30-seconds-of-code/blob/bd756f8b2283a38560efe83cb30af38324d4e9e9/src/lib/contentUtils/tocReader.js#L5-L7)

Source repository: https://github.com/Chalarangelo/30-seconds-of-code
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/30-seconds-of-code.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
