trellis_site 0.11.1 copy "trellis_site: ^0.11.1" to clipboard
trellis_site: ^0.11.1 copied to clipboard

Static site generator for Trellis — Markdown content, Trellis templates, Hugo-inspired conventions.

Changelog #

0.11.1 #

Changed #

  • Lattice theme: new show_site_demo param (default false) renders a Markdown-to-page block on the home page from a site_demo block in data/lattice.yaml; dropped whole when a required key is missing, like the other home sections. The Trellis docs site enables it, gains a top-level Sites section, and ships examples/docs_site/.

Fixed #

  • README install snippet pinned trellis_site: ^0.1.0, a range no published version of the package matches, so a copied snippet failed dart pub get. It now tracks the lockstep version and tool/version_lockstep.sh keeps it current.

0.11.0 #

Breaking #

  • Symlinks under static/ are no longer published. Both static-asset walks – the site's static/ and an installed theme's – now list with followLinks: false, so a symlinked file or directory is skipped instead of copied. This closes a containment hole: a static/secrets -> ~/.ssh link, committed in a site or in the third-party code of a theme installed with trellis theme add, published its target into output/ with no warning and a zero exit. It also drops symlinks that were there on purpose – a static/ link into a shared asset directory stops appearing in the built site and BuildResult.staticFileCount falls to match, with no warning. Put the real files under static/, or copy them in before trellis build.
  • Symlinks under content/ are no longer rendered. Content discovery also lists with followLinks: false, so a symlinked Markdown or HTML file cannot publish a readable file outside the site. The build emits a warning for each skipped content symlink. Replace intentional links with real content before building.

Added #

  • PageGenerator.emittedPages exposes the absolute HTML paths written by the most recent generateAll pass, including paginated outputs and excluding files merely copied from static/. The list resets at the start of each pass.
  • BuildResult.themeShadowedLayouts always lists the active theme layouts shadowed by site layouts, whether or not the resulting theme is inert. BuildResult.themeIsInert separately reports that the theme's stylesheets and scripts were published but no emitted page linked any of them. The inertness check covers the theme's static/ .css/.js, generated css/theme-props.css, and compiled sass/ output; images and fonts are excluded, since a page can carry a theme favicon and still be unstyled. A site may therefore report shadowed layouts while themeIsInert remains false when its rendered shell still links a theme asset.
  • shadowedThemeLayouts({siteDir, siteLayoutsDir, themeDir}), exported from package:trellis_site/trellis_site.dart, returns the layouts a theme provides that the site also provides at the same relative path, as site-root-relative paths. Those are the layouts site-first resolution makes unreachable; it is what populates BuildResult.themeShadowedLayouts and what trellis theme add reports at install time.
  • resolveFrontMatterDate(value) – the front-matter date: resolver that FeedGenerator and SitemapGenerator now share, exported from package:trellis_site/trellis_site.dart. Returns a UTC DateTime, or null when the value is absent or unparseable so callers fall back to the source file's mtime.

Changed #

  • baseUrl is normalized to drop its trailing slash when the config loads (new static SiteConfig.normalizeBaseUrl). baseUrl: https://example.com/ previously reached templates verbatim, and ${page.url} is always root-absolute, so the ${site.baseUrl} + ${page.url} themes use for og:url emitted a doubled slash – https://example.com// on the home page. ${site.baseUrl} now renders as https://example.com, and https://example.com/base/ as https://example.com/base; a baseUrl already written without the slash is unchanged. A value made only of slashes is left alone, because emptying it would silently switch off sitemap and feed generation, which both gate on a non-empty base. sitemap.xml and the feeds carry their own trailing-slash trim and are unaffected. A template that joins a relative path onto ${site.baseUrl} and relied on the configured trailing slash to separate them now has to write the slash itself.
  • Zone-less front matter dates are resolved reproducibly in UTC (TD-014). date: 2026-01-01 previously parsed as local midnight, so feed.xml <updated> and rss.xml <pubDate> depended on the build machine's timezone – the same content emitted 2025-12-31T23:00:00Z on a CET laptop and 2026-01-01T00:00:00Z on a UTC runner. Feed timestamps are now identical on every machine. A zone-less date-time such as 2026-03-15T09:30:00 likewise names UTC calendar fields instead of the build machine's local time; an explicit Z or numeric offset remains authoritative. Sites that relied on local-time interpretation should add their intended offset. sitemap.xml <lastmod> uses the same resolver and is timezone-stable for date-only, zone-less, and explicit-offset values.
  • A theme: that resolves to no installed theme now names the command that installs it. The ThemeManifestException reads Theme 'x' not found in themes/ — install it first, e.g. 'trellis theme add <url-or-path> --theme x' instead of stopping at the directory that is missing.

Documentation #

  • The README's built-in Verdant install now uses trellis theme add https://github.com/tolo/trellis with --theme verdant --ref v0.11.0. It previously pointed at a per-theme repository (tolo/trellis-theme-verdant) that does not exist – the themes live under themes/ in the one repository.

Notes #

  • The Trellis repository gained three themes in this release – Lattice (documentation), Folio (reference) and Meadow (product landing), joining Arbor, Bloom and Verdant. They are not part of this package and do not travel through pub.dev: themes live under themes/ in tolo/trellis and are installed into a site with trellis theme add https://github.com/tolo/trellis with --theme <name> --ref v0.11.0. What this package provides is the machinery that discovers, loads, merges the params of and renders them.

0.10.2 #

Changed #

  • Lockstep version bump to keep all Trellis SDK packages on a single shared version. No functional changes in this package.

0.10.1 #

Fixed #

  • Build time no longer grows quadratically with the number of pages in a single section. ${page.prev}/${page.next} resolution re-sorted the whole section for every page it rendered, so a flat blog with thousands of posts in one section slowed sharply (a 4000-post section spent ~8 s on neighbour resolution alone; 2000 posts ~1.9 s). The section ordering is now computed once per build pass. A full build of a 2000-post flat blog now spends ~350 ms in total page generation. Sites with pages spread across many sections were barely affected and see no change.

Added #

  • benchmark/site_scale_benchmark.dart – a scale benchmark for the page-generation hot paths, run by hand when touching orderedSectionPages, prev/next resolution, or NavigationBuilder. Reports growth ratios across site sizes so superlinear behaviour is visible, includes the flat single-section worst case, and times a full generateAll on that worst case so the memoized path is measured where it lives.

0.10.0 #

Added #

  • Per-page taxonomy term links (${page.termLinks.<taxonomy>}): every content page now exposes its own front-matter terms, per declared taxonomy, as a list of {name, slug, url, count} maps whose url is the canonical slugified term-page path (/tags/hello-world/) resolved from the same index that generates the term pages. Themes link tags via ${tag.url} instead of string-building /{taxonomy}/{rawTerm}/. The key is present only when the taxonomy is declared and the page has terms, so templates not reading it are byte-for-byte unchanged; ${taxonomy.<name>} remains the site-global term list.
  • Build-time syntax highlighting (ADR-010): fenced Markdown code blocks are highlighted at build time by a new CodeHighlighter (backed by package:highlight), which emits highlight.js-style .hljs-* token spans directly into the built HTML — no client-side highlighter JS is shipped or fetched. Controlled by a new top-level highlight: key in trellis_site.yaml, on by default; set enabled: false under it to fall back to plain <pre><code>. Unknown or unspecified languages are left unhighlighted.

Changed #

  • Syntax-highlighting token CSS migrated from Prism .token.* to .hljs-* (ADR-010) across the official themes, to match the new build-time output. The per-theme syntax_highlighting param is retired — highlighting is now controlled site-side via the highlight: config key, not per theme. Migration: replace theme_params: { syntax_highlighting: false } with highlight: { enabled: false } in trellis_site.yaml; the old param is now ignored with an Unknown theme param warning.

Fixed #

  • Theme SASS bridge hardening (follow-up review of the 0.9.1 fix): string params are now emitted via SASS interpolation (#{"..."}) instead of the deprecated global unquote() (removed in Dart Sass 3.0.0), eliminating the global-builtin deprecation warnings 0.9.1 introduced. A SASS interpolation marker (#{...}) in a param value is neutralized to literal text — never evaluated — across every emit path: string values, the hex-color fast-path (now gated on a real hex pattern rather than string length, so an interpolation-shaped value like #{9} can no longer slip through raw and be evaluated), color-typed values, and map keys. Multiline (newline-containing) param values no longer abort the SASS compile (escaped as CSS \a). (Structural characters ;{} in a value remain a separate, tracked robustness limitation — see TD-011.)
  • Verdant _default/single.html tag pills broke the build for any tagged post. The pills built each tag URL as @{/tags/{tag}/} — invalid Trellis URL-expression syntax (@{...} supports query params, not {path} templating) — so rendering a post with show_tags on threw an ExpressionException and aborted the build. Even had it parsed, interpolating the raw tag produced /tags/Hello World/ while the term page lives at the slugified /tags/hello-world/ — a 404 for any tag with uppercase, spaces, or punctuation. Pills now iterate ${page.termLinks.tags} and link via each term's canonical url, matching the generated term page exactly. The bundled example/ site now declares taxonomies: [tags] so its term pages are generated and the pills resolve.
  • Verdant example/ site was not buildable standalone. Two problems, both now fixed to match the working arbor example: (1) theme: .. never resolved — theme resolution joins <siteDir>/themes/<value>, so .. pointed at the example dir, not the theme; the example now ships a git-tracked themes/verdant → ../.. symlink and sets theme: verdant. (2) The example's home.html override wrapped an already-complete page URL in a @{...} URL expression (@{${p.url}}), a parse error; it now links via ${p.url} like the theme's own layout. trellis build on themes/verdant/example is green (8 pages, 0 broken links).

0.9.1 #

Fixed #

  • Theme SASS bridge: string params compiled into invalid quoted CSS values. String-typed theme params (font_family, max_width, ...) were written to the generated _theme_params.scss as quoted SASS strings; the bridge loads before the theme's _variables.scss, so the quoted value won and compiled into invalid CSS (font-family: "system-ui, ..." — ignored by browsers, falling back to serif; max-width: "1200px" — dropped, unconstraining the layout). Affected every theme built with a params bridge (arbor and verdant alike). The generator now emits values via unquote() with escaping for embedded quotes/backslashes.

0.9.0 #

Added #

  • URL path-prefix (pathPrefix): serve a site under a sub-path (e.g. GitHub project pages at /my-site/) via a new pathPrefix config key. When set, every engine-derived internal URL -- page links (${page.url}), the section/menu tree, prev/next, and search-index url fields -- resolves under the prefix, and sitemap.xml/feeds compose baseUrl with the prefixed URL so the prefix appears exactly once. The value normalizes leading/trailing slashes to a canonical /x/ form (my-site, /my-site, my-site/, /my-site/ all → /my-site/); '' and / mean root-served. Absolute/external URLs are left untouched. An un-normalizable value (scheme-bearing/protocol-relative URL, non-string, whitespace or : anywhere, a ./.. path segment, or an empty interior segment such as a//b) aborts the build with a SiteConfigException and writes no output. With no pathPrefix (the default) output is byte-for-byte unchanged. The normalized prefix is exposed to templates as ${site.pathPrefix} so theme authors can prefix hand-written literal asset references, e.g. tl:href="${site.pathPrefix} + 'css/main.css'". The prefix lives in emitted URLs only, not in the on-disk output layout — files are written at their unprefixed paths so the whole output/ tree can be published as the site root and mounted under the prefix by the host (e.g. GitHub Project Pages). Hand-written root-absolute internal links and asset references in the emitted HTML (Markdown content links, theme literals, <script src>/<link href>) are automatically rewritten to carry the prefix (expected SSG behavior); relative links, in-page anchors, external URLs, escaped code examples, and already-prefixed engine URLs are left untouched.
  • Weighted page ordering: pages may declare an integer weight front-matter field; within a section, weighted pages sort first by ascending weight, then ties and all unweighted pages fall through to the existing date-descending-then-URL order. Sections with no weight are byte-for-byte unchanged. A malformed weight (non-integer) is ignored with a build warning naming the page and treated as unweighted; the build never aborts.
  • Nested-section lineage: new additive Page.sectionPath field exposes a page's full nested section lineage (e.g. docs/guides), available in templates as ${page.sectionPath} (string) and ${page.ancestors} (cumulative list). ${page.section} is unchanged and remains the top-level folder.
  • A nested _index.md section listing now contains exactly its own-level pages and excludes sibling sub-sections' pages.
  • orderedSectionPages(sectionPath, allPages) -- a reusable engine function returning a section's own-level content pages in canonical (weight-aware) order. The ${pages} listing, pagination, and downstream ordering consumers route through this single function.
  • Hierarchical navigation menu (${site.menu}): a nested navigation tree mirroring the content section hierarchy, available to every page render (single, section, home, and taxonomy virtual pages -- not only list pages). Each node is a {title, url, children} map; own-level pages within a section are ordered via orderedSectionPages (same canonical weight-aware order as ${pages}), and sections nest by Page.sectionPath lineage. The tree carries no per-page active flag -- it is built once and shared across renders, so a theme marks the active page and its ancestor trail at render time by comparing each node.url against ${page.url}. A node's title follows a 3-tier precedence: menu_title front matter → title front matter → humanized URL slug (getting-startedGetting Started). Sibling section nodes are ordered by their section page's (_index.md) weight (ascending, weighted before unweighted), then lexically — so a docs IA can order its sections deliberately; with no section weights the order is the previous pure-lexical order (backward-compatible). Draft pages and pages with menu_exclude: true are excluded (a section that opts out drops its own node but hoists surviving children to its parent; a folder with no _index.md still yields a synthesized node titled from the humanized folder name). Empty or fully-excluded content yields an empty list (never null). Sites whose templates do not reference ${site.menu} are byte-for-byte unchanged.
  • Front-matter keys menu_title (string) and menu_exclude (bool): control a page's ${site.menu} node title and inclusion.
  • In-section prev/next (${page.prev} / ${page.next}): every single doc page exposes references to its immediate neighbors within its own section, each a {url, title} map, so a doc reads straight through in its authored order. Neighbors follow the section's canonical (weight-aware) order via orderedSectionPages -- the same ordering as ${pages} and ${site.menu}, with no separate prev/next sort (weighted sections read in weight order, not date order). Boundaries are handled by absence: the first page has no prev, the last has no next, and a single-page section has neither, so a tl:if-guarded region renders only the links that exist. Only single doc pages receive these keys; section, home, and taxonomy pages do not (list-page sequencing stays with ${pagination.*}). Neighbor title uses the same 3-tier fallback as ${site.menu} (menu_titletitle → humanized URL segment), so a titleless neighbor still renders a readable label. Sites whose templates do not reference ${page.prev}/${page.next} are byte-for-byte unchanged.
  • Structured breadcrumbs (${page.breadcrumbs}): every page exposes a structured breadcrumb trail -- one {url, title} node per ancestor section, shallow-to-deep (mirroring ${page.ancestors}). Titles use the ${site.menu} 3-tier fallback (menu_titletitle → humanized folder segment) so labels read like the menu (Guides, not docs/guides); URLs are the section's own path-prefix-aware URL (empty for a folder with no _index.md). Additive to the unchanged ${page.ancestors} cumulative-path list.

Fixed #

  • Relative theme: paths now resolve. A theme: value that escapes the site directory (e.g. theme: ../../themes/arbor, used when a site references a repo-root theme) is now normalized so both the manifest loader and the template FileSystemLoader resolve a real path. Previously the un-collapsed .. segments (through a non-existent <siteDir>/themes directory) made the template loader throw "base path does not exist". Bare theme names (theme: verdant) are unaffected.

0.8.2 #

Changed #

  • Lockstep version bump to keep all Trellis SDK packages on a single shared version. No functional changes in this package.

0.8.1 #

Changed #

  • Lockstep version bump to keep all Trellis SDK packages on a single shared version. No functional changes in this package.

0.8.0 #

Changed #

  • Version aligned to the unified Trellis SDK lockstep versioning scheme — all SDK packages now share a single version number and are released together. No functional changes since 0.2.0.

0.2.0 #

Added #

  • Theme system: ThemeManifest, ThemeConfig, and ThemeParamMerger for parsing theme.yaml manifests and deep-merging theme params with site overrides.
  • ThemeAwareLoader for site-first, theme-fallback layout resolution with theme: prefix support for cross-boundary tl:extends.
  • ThemeSassGenerator for auto-generating _theme_params.scss (SASS variables) and _theme_custom_props.css (CSS custom properties) from merged theme params.
  • ThemeBuildConfig and SkinMode for SASS bridge configuration and skin file resolution (light, dark, auto).
  • Theme static asset merging (theme static/ copied to output, site static/ wins on conflict).
  • Theme data file merging (theme data/ as fallback, site data/ wins per-file).
  • ${theme.*} template context namespace for accessing merged theme params.
  • Build warnings for unknown theme_params: keys.
  • siteVersion constant for runtime version identification.

Changed #

  • SiteConfig extended with themeConfig field (parsed from theme:, theme_ref:, theme_params: in trellis_site.yaml).
  • BuildResult extended with optional ThemeBuildConfig for CLI SASS compilation.
  • PageGenerator accepts external TemplateLoader, layoutSearchPaths, and themeDataDir for theme-aware builds.
  • SASS load path order: .trellis/build/ (bridge) → site/sass/theme/sass/.

0.1.0 #

Added #

  • ContentDiscovery for recursive Markdown scanning, page-bundle support, and Hugo-style URL derivation.
  • FrontMatterParser for YAML front matter extraction and validation.
  • MarkdownRenderer for GitHub-flavored Markdown, summary extraction, and table-of-contents generation.
  • PageGenerator for layout resolution, data cascade assembly, and page rendering.
  • TrellisSite for full build orchestration across content, layouts, data, static assets, and output.
  • BuildResult and BuildWarning for build reporting.
  • SiteConfig for trellis_site.yaml loading with configurable content, layouts, static, data, and output directories.
  • TaxonomyCollector and Paginator for section, taxonomy, and list-page generation.
  • ShortcodeProcessor for pre-Markdown and post-Markdown shortcodes rendered through Trellis templates.

Generated Output #

  • SitemapGenerator for sitemap.xml.
  • FeedGenerator, FeedConfig, and FeedResult for Atom, RSS, and per-section feeds.
  • SearchIndexGenerator and SearchConfig for JSON search indexes compatible with client-side search tools.

Content Features #

  • Global data file loading from data/*.yaml.
  • Draft filtering, page bundle asset copying, and site-level params exposed to templates.
0
likes
160
points
364
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Static site generator for Trellis — Markdown content, Trellis templates, Hugo-inspired conventions.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

highlight, html, markdown, path, trellis, yaml

More

Packages that depend on trellis_site