Watch
1
0
Fork
You've already forked chobble-template
0
mirror of https://github.com/chobbledotcom/chobble-template.git synced 2026-10-17 12:32:24 +01:00
The Chobble template - an Eleventy site, built with Nix https://example.chobble.com
  • JavaScript 86.4%
  • SCSS 5.7%
  • HTML 4.5%
  • XSLT 2.8%
  • TypeScript 0.3%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
stefan burke 799782aaa4
Improve CI build efficiency and resource metrics (#1481)
* Improve CI build efficiency and metrics

* Make indexed git dates exactly match the legacy per-file behavior

The template-only history pathspec hid renames and copies whose source
was a non-template file, so their targets recorded the transfer date as
the published date. Scan the full history with rename detection (cheap
without copy detection) and pair it with a template-path copy-only scan,
so transfer targets inherit the source's original publish date through
rename chains.

* Fix CI findings: deduplicate the scan, cover the git date modules

The full cpd gate flagged the duplicated record-header parse and the
twin git log invocations; extract splitHistoryRecord and scanHistory so
both scans share one implementation.

tests:unit enforces 100% line and function coverage, and three regions
were untested: the eleventy.before hook and filter bodies, the
assertGitSuccess failure branch, and the copy-origin rename-descendant
recursion. Invoke the registered handler and filters in the plugin
test, break a repo HEAD to exercise the failure path, and cover a copy
followed by a rename.

* Feed the parsed record to recordTransfers

jscpd normalizes identifiers, so the split-then-guard heads of
parseHistoryRecord and recordTransfers still normalized to an identical
fragment over the min-tokens threshold. recordTransfers now takes the
parsed { date, tokens } from scanHistory, leaving one split site.

* Wait for autosizes FCP restoration with a deadline

The FCP restoration tests asserted a fixed 50ms after runAutosizes, but
restoration runs through chained happy-dom timers, and under the
concurrent unit suite on a small CI runner the restore task lands past
that window once heavier test files share the CPU. Poll with a 2s
deadline instead of sleeping a fixed 50ms.

* Restore legacy merge semantics and count mode-only changes

The unrestricted full-history scan replayed every reachable branch
commit, so a conflict resolution that kept one parent's version of a
template still recorded the rejected parent's commit as the updated
date, and novel conflict resolutions missed the merge date. The
template pathspec scan is back as the primary index: its history
simplification is exactly what the legacy per-path queries saw.

The cheap full-history rename scan stays as a side channel: renames
whose source is outside the template pathspec get their original add
date backfilled with one targeted follow query and propagated through
rename descendants. Non-template rename targets are skipped, so the
backfill costs one query per cross-extension rename into a template.

Mode-only commits (100644 to 100755) keep the same blob, so they were
treated as no-ops and the updated date went stale; the raw change now
carries both modes and updates on a mode flip.

* Pin the test git repos to a main branch

git init on CI has no init.defaultBranch set, so the default branch
there is master and the merge test's checkout main failed. init -b
main makes the fixture deterministic in every environment.

* Handle novel merge resolutions, copied renames, and scan cost

Three review findings:

- A merge that resolves a template to content different from every
  parent is interesting to git's simplified history and legacy git log
  counts it, but --raw emits no diff for merges by default, so the
  index stopped at the last parent-side commit. A merges-only scan now
  replays first-parent diffs; records older than a path's recorded
  update are skipped so real merges cannot regress newer commits.
- A template copied after a cross-extension rename inherited the
  pre-backfill synthetic date; copy records from the template scan now
  join renames as propagation edges for backfilled origins.
- renameRecords built and discarded the full dates map; it now filters
  to transfer records and skips date reduction, and the parse state
  machine collapsed into a shared recordChanges extractor used by both
  replay paths.

* Apply merge diffs positionally and anchor rename origins

- replay novel merge resolutions at their position in the simplified
  history instead of comparing author dates, fixing skewed author dates
  (cherry-picks, rebases) being skipped or applied out of order
- replace a stale published date when a rename reuses a deleted path
- anchor rename-origin lookups at the transfer commit so later
  incarnations of the source path cannot leak their dates
- remove the Claude Code and reviewdog GitHub workflows

* Use function declarations for multi-line helpers

bun 1.3.6's coverage instrumentation intermittently counts an
unassigned continuation line of a multi-line arrow as uncovered,
which fails the 100% coverage threshold in CI with all tests
passing. Function declarations attribute each line reliably.

* ci: dump git-dates coverage record on failure

* Make guard-line coverage independent of git copy detection

The patchOriginChain rejection return only executed when git reported
the successor as a copy record, which varies by git version, so CI's
coverage threshold failed with all tests passing. Route the guard
through a lineageMatches helper whose lines execute on every call, and
use function declarations for multi-line helpers to keep every line
attributed under bun 1.3.6's instrumentation.

* Stop rename-origin propagation at visited paths

A rename cycle (a.md to b.md back to a.md) whose commits share one
author timestamp satisfies the lineage guard on every hop and recursed
until the call stack overflowed, aborting the build. Track the paths
already visited in the walk.

* Remove generated devenv state from tracking

The .devenv directory is machine-local runtime state that predates
main's .gitignore entry; it was tracked by an add -A in this branch
before the merge brought the ignore rule.

* Key merge resolutions by commit and skip inherited adds

- pair each merge's first-parent diff with its date-only record by
  commit hash, since author dates can collide across sequential merges
- replay only paths whose merge result differs from every parent
  (detected via a combined-diff scan), so files inherited unchanged
  from the second parent keep their own update dates
- remove the generated .devenv state directory that predates main's
  .gitignore entry

* Mutate the indexByCommit accumulator in place

The map copy per matching record made building the merge indexes
quadratic in the number of merges; the reducer owns its accumulator,
so mutating it is safe.

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Chobble <code@chobble.com>
Co-authored-by: chobble-bot <bot@chobble.com>
2026-10-10 21:10:50 +00:00
.claude/agents Extract shared js-toolkit package from utility code (#749) 2026-01-19 01:04:05 +00:00
.forgejo/workflows Improve generated SEO metadata (#1473) 2026-07-13 16:30:52 +00:00
.github Improve CI build efficiency and resource metrics (#1481) 2026-10-10 21:10:50 +00:00
.zed Add jscpd for copy/paste detection (#51) 2025-11-28 13:53:06 +00:00
bin Clickable menu cards (#1452) 2026-06-27 12:42:31 +00:00
packages/js-toolkit Reduce test timeouts, optimize single-use-function analysis, and refactor integration tests (#1453) 2026-06-28 00:03:37 +00:00
screenshots Improve reviews layout design (#742) 2026-01-18 16:46:37 +00:00
scripts Improve CI build efficiency and resource metrics (#1481) 2026-10-10 21:10:50 +00:00
src Improve CI build efficiency and resource metrics (#1481) 2026-10-10 21:10:50 +00:00
test Improve CI build efficiency and resource metrics (#1481) 2026-10-10 21:10:50 +00:00
.claude.json Add PostToolUse lint hook for Write/Edit operations (#559) 2026-01-09 18:51:27 +00:00
.codex Search box wrapper (#1415) 2026-05-27 21:20:28 +00:00
.eleventy.js Fix raw HTML rendering in markdown by disabling indented code blocks (#1435) 2026-06-12 09:10:58 +00:00
.envrc Use system nixpkgs 2025-12-11 00:20:24 +00:00
.gitignore Add devenv environment for precommit checks (#1490) 2026-10-09 17:33:49 +00:00
.jscpd.json Add click-sparkle WebGL effect to about page (#1361) 2026-04-25 02:50:35 +00:00
.pages.yml Tag guide pages with a property, keep property guides off the site-wide guide (#1483) 2026-08-17 16:34:14 +00:00
.stylelintrc.json Generic-ify FAQs (#1417) 2026-06-03 20:28:07 +00:00
biome.json Clickable menu cards (#1452) 2026-06-27 12:42:31 +00:00
BLOCKS_LAYOUT.md Tag guide pages with a property, keep property guides off the site-wide guide (#1483) 2026-08-17 16:34:14 +00:00
bun.lock Bump happy-dom to ^20.14.5; make autosizes robust to img.complete (#1488) 2026-09-28 13:34:09 +00:00
bunfig.toml Add a mutation testing tool ("tests for your tests") (#1458) 2026-06-29 00:45:41 +00:00
CLAUDE.md Add a mutation testing tool ("tests for your tests") (#1458) 2026-06-29 00:45:41 +00:00
devenv.lock Add devenv environment for precommit checks (#1490) 2026-10-09 17:33:49 +00:00
devenv.nix Add devenv environment for precommit checks (#1490) 2026-10-09 17:33:49 +00:00
devenv.yaml Add devenv environment for precommit checks (#1490) 2026-10-09 17:33:49 +00:00
flake.lock Move page content into blocks and replace base.html with the design-system layout (#1428) 2026-06-11 18:15:32 +00:00
flake.nix Add quote cart controls to menu items (#1449) 2026-06-26 16:02:12 +00:00
knip.json Add stylelint with property sorting to surface SCSS duplicates (#1395) 2026-05-05 00:43:34 +00:00
LICENSE Initial commit 2025-03-18 17:36:20 +00:00
package.json Bump happy-dom to ^20.14.5; make autosizes robust to img.complete (#1488) 2026-09-28 13:34:09 +00:00
README.md Update chobble.com links to www.chobble.com (#1485) 2026-08-31 22:04:46 +00:00
tsconfig.json Add click-sparkle WebGL effect to about page (#1361) 2026-04-25 02:50:35 +00:00
tsconfig.strict.json Add strict typecheck ratchet and type generation freshness test (#1088) 2026-02-17 14:31:05 +00:00

The Chobble Template

⚠️ Don't forget to change the Formspark and Botpoison info in _data/config.json or in your repository's action secrets ⚠️

See this template in action at:

Want me to make you a website based on this template? Hit me up at chobble.com.

💖 Want to support the development of this template? 💖 Donate at liberapay.com/chobble

An Eleventy starter for business websites. The GitHub action deploys to both Neocities and Bunny.net - you'll need to edit that for your setup.

Content Types

  • Products - galleries, options with SKUs, FAQs, specifications, features lists, linked reviews with ratings
  • Categories - product groupings with inherited thumbnails
  • Events - one-off and recurring schedules, iCal feed generation
  • News - blog posts with Atom feed
  • Menus - categories, items, pricing, dietary indicators (vegan, gluten-free, etc.)
  • Locations - multi-site support with sub-locations
  • Properties - for holiday lets, linked to locations
  • Reviews - linked to products, aggregate ratings
  • Team - member profiles
  • Snippets - reusable content blocks

Shopping Cart & Payments

  • LocalStorage-based cart with quantity limits
  • Stripe/Square checkout via external ecommerce backend
  • Quote/enquiry mode (submit cart as request instead of payment)
  • Auto-generated SKUs via GitHub Action

Theming

  • 10 pre-built themes: Default, Neon, 90s Computer, Floral, Hacker, Monochrome, Ocean, Old Mac, Rainbow, Sunset
  • Per-page theme overrides
  • Visual theme editor at /theme-editor/ with export
  • CSS custom properties for colours, fonts, borders, layout
  • SCSS support
  • Bunny Fonts integration

Images

  • Responsive images with srcset via eleventy-img
  • Base64 LQIP placeholders
  • Gallery component with thumbnail navigation and full-size overlay
  • Custom aspect ratio cropping

Forms

SEO & Structured Data

  • Schema.org markup for products (with reviews/ratings), events, FAQs, organisation
  • Canonical URLs
  • Sitemap
  • Atom feed with XSL stylesheet
  • Meta descriptions, noindex support
  • hreflang tags and og:locale for sites published in more than one language

Languages

A site is written in one language until it says otherwise, and nothing in the template names a language.

  • _data/languages.json lists every language the site publishes, each with a code, hreflang, og_locale, label, home_url prefix, home_label and breadcrumb_label. Exactly one entry has is_default: true, which is the base language: the one every URL outside another language's prefix is written in, and the one hreflang="x-default" points at.
  • _data/translations.json pairs the pages that say the same thing, keyed by language code, e.g. [{ "en": "/about/", "de": "/de/ueber-uns/" }].

A page's language comes from its URL prefix, and the layout, the head tags, the footer switcher and the breadcrumbs read it as pageLanguage. Exactly one language must be marked is_default, and a site that marks none fails the build. To add a language, add its entry, put its pages under its home_url, list them in translations.json, and translate _includes/footer.html and the navigation for it. A page with no counterpart in a language links that language's home page from the footer.

The template ships one language and no translations, which renders exactly as it did before this existed: no hreflang tags and no switcher.

What is translated is pages. Collection items (products, events, categories, properties, guide categories) have no language of their own: they are looked up by slug across the whole collection, and their labels come from _data/strings.json, which has no language dimension. A translated page that sits under a collection gets its index URL from the translation groups where a site has paired one, but the crumb's label, and any category or property crumb below it, still come from the base language. Translating a collection needs a language on its items and per-language strings, which is its own change.

Navigation & Layout

  • Horizontal or left sidebar navigation
  • Sticky mobile nav option
  • Two-column layout with sidebar
  • Slider component for horizontal scrolling
  • Scroll-fade animations (respects prefers-reduced-motion)

Development

  • Nix flakes with direnv support
  • bin/lint - format with Biome
  • bin/screenshot - automated screenshots
  • Biome linting
  • jscpd duplicate detection
  • Knip unused code detection
  • 17+ test files with custom runner
  • instant.page for link prefetching on hover

Testing template code in a downstream client site

Sites built from this template copy it excluding test/, test-*, images/, and markdown content, then overlay their own site content under src/. That missing test/ directory means the suite can't run at all from a checkout in that state, and even with it copied back in by hand, fixture-driven tests fail, because the test fixture factory (test/test-site-factory.js) builds its "template defaults" (config, sample content) from the live checkout root — which is now your own site content, not the template's.

scripts/stage-hermetic-tests.js handles both problems for you. Point it at a separate, pristine template checkout and it creates a temporary workspace containing your downstream template code plus pristine template tests, data, markdown, and image fixtures. It runs the suite there, removes the workspace, and forwards the suite's exit status without changing your checkout:

git clone --depth 1 https://github.com/chobbledotcom/chobble-template.git /tmp/chobble-template-upstream
bun scripts/stage-hermetic-tests.js --template /tmp/chobble-template-upstream -- test/integration

This also points the fixture factory at the pristine checkout via the CHOBBLE_TEMPLATE_FIXTURES_DIR env var, so fixture tests build isolated sites from template defaults rather than your overrides. bun run test:hermetic -- --template <path> -- <args> runs the same thing via the package.json script.

Deployment

  • GitHub Actions workflow for Neocities and Bunny.net
  • Forgejo Actions support
  • PagesCMS integration for no-code editing
  • External ecommerce backend for payment processing

Configuration

  • _data/config.json - Formspark, Botpoison, ecommerce checkout, map embed, nav options
  • _data/site.json - name, URL, description, social links (14 platforms), opening hours
  • _data/meta.json - language, organisation details for schema.org
  • _data/languages.json - languages the site publishes, and which is the base
  • _data/translations.json - pages that say the same thing in each language
  • _data/strings.json - customisable labels and permalink directories

Want a website based on this template? Clone this repo, or hit me up at Chobble.com.