# Extension points — Chalarangelo/30-seconds-of-code

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

# Extension points — Chalarangelo/30-seconds-of-code

Scanned commit bd756f8b2283a38560efe83cb30af38324d4e9e9 (2026-09-09). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

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)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

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