Chalarangelo/30-seconds-of-code
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.
- 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.
- 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.
- 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)
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.