---
title: 'v0.1.0-next.48'
description: 'Dates get resolved from sources you choose instead of file timestamps, assets get a hashed index, and a sitemap finally emits the changefreq and priority you authored.'
slug: '0-1-0-next-48'
type: 'page'
date: '2026-10-11'
lang: 'en'
url: 'https://www.eleventy-baseline.dev/release-notes/0-1-0-next-48/'
---

The big one is dates. Baseline no longer uses Eleventy's `page.date`, which on CI is the moment your repository was cloned rather than anything to do with your content. It resolves a publish and a modified date from an ordered list of sources you can change, keeps a calendar date as a calendar date, and emits nothing at all when it has nothing to go on.

That last part is the change most likely to show up in your output. A page with no date now carries no date. **Read the first breaking note before you upgrade**, because the fix is one line of config and the symptom is quiet.

Second, built asset filenames carry a content hash, so you can finally serve them `immutable` for a year. Anything declared in `settings.head` is resolved for you, so most sites need no edit; a layout naming a file directly gets the new `asset` filter. Entry points are compiled once at the start of the build and recorded with their hash, size and SRI value, readable from a template.

And three smaller ones: a sitemap emits the `changefreq` and `priority` you wrote in front matter, which it has never done; a page-break marker can name the first part of a split page; and a declared asset stops being reported missing when fingerprinting renamed it.

---

## Breaking

{% stepsBlock 'compact' %}

- **A page with no resolvable date now has no date.**

  `datePublished` used to fall back to Eleventy's `page.date`, which Eleventy fills in from the file's creation time when your front matter says nothing. On a CI build that is the clone time, so every page in a build shared one publish date, to the millisecond, and it moved forward on every deploy. It usually landed after the modified date.

  Baseline now tries, in order: your `datePublished` key, your `date` key, then a `YYYY-MM-DD` in the file name. If none of those answer, the field is absent. The JSON-LD omits the property, and the sitemap entry has no `<lastmod>` element.

  That is deliberate. `datePublished` is optional on a `WebPage`, and an absent date is honest where a wrong one is not.

  - _Symptom: `datePublished` disappears from the structured data on pages that never had a real date, and some `<lastmod>` elements leave your sitemap. If you want them back, put the date in front matter, or add `':git-created'` to the source list and accept that it dates a page to its first commit._

- **A calendar date stays a calendar date.**

  `datePublished: 2026-04-28` in your front matter now emits `2026-04-28`, where it used to emit `2026-04-28T00:00:00.000Z`. Both the sitemap's `<lastmod>` and schema.org's date properties accept a day on its own, so nothing is lost, and the day no longer shifts for a reader in another time zone.

  Add a time if you want an instant. A time with no offset is read in `settings.timeZone`, which is new and defaults to UTC.

  - _Symptom: shorter date strings in your JSON-LD and your sitemap. Validators accept both forms._

- **The `date` template global is gone.**

  It had one method, `date.toUTCISO(value)`. Every date Baseline emits now arrives at the template as a finished string, so there was nothing left for it to convert, and it was the last place a calendar date could be widened back into a midnight timestamp.

  - _Symptom: a template calling `date.toUTCISO(...)` throws. Print the value directly instead. For a human-readable date, reach for `Intl.DateTimeFormat` with the locale and zone Baseline resolved for the page._

- **`page.date` is no longer on the page context, and `date` is no longer on a content graph node.**

  Both carried Eleventy's raw date object. They now carry `datePublished` and `dateModified` as resolved strings, in both places: `_pageContext.page` and every entry in `_navigator.nodes`.

  - _Symptom: `_navigator.nodes[url].date` and `_pageContext.page.date` are `undefined`. Eleventy's own `page.date` is untouched and still there if you want it._

- **Built asset filenames now carry a content hash.**

  `assets/css/index.css` is written as `assets/css/index.342303cd.css`, and the same for JS entry points. Only in a `build`; `--serve` keeps the plain names so the dev server stays predictable.

  The point is caching. A stable filename makes a long `Cache-Control` header unsafe, which is why every project ends up hand-rolling a `?v=` query string. With the hash in the name you can serve assets `immutable` for a year.

  **Anything you declared in `settings.head` is resolved for you**, so a site that routes its stylesheets and scripts through settings changes nothing and needs no edit. The new `asset` filter covers a layout that names a file directly. Set `assets: { fingerprint: false }` to turn it off.

  - _Symptom: your `dist/assets/` filenames change, and a hardcoded `/assets/css/index.css` in a template that goes through neither `settings.head` nor the `asset` filter now 404s. Grep your layouts for literal asset paths._

- **The sitemap's computed entry moved to `_sitemap`.**

  It was written to `eleventyComputed.page.sitemap` and read from `item.data.sitemap`, so the two never met. Three of the four values it carries were unreachable as a result.

  Your `sitemap` front-matter key is unchanged and is still where you author `ignore`, `lastmod`, `changefreq` and `priority`. Only the resolved object moved.

  - _Symptom: none, unless you read `page.sitemap` in a template of your own, which would have been reading the dead defaults layer._

{% endstepsBlock %}

---

## Added

- **`settings.timeZone`.** Optional, defaults to `UTC`. An IANA name such as `'Europe/Amsterdam'`. It anchors a date-time written without an offset; a value carrying its own offset is converted from that, and a calendar date is not a moment, so neither is affected.

  Validated by handing it to `Intl`, so any name, alias or casing your platform accepts works. A bare offset like `+02:00` is refused, because it has no daylight saving and would be wrong for half the year.

- **`options.dates`.** Replace the source list for either field. `:default` expands to the built-in list in place, so you can prepend a key of your own without restating the rest:

  ```js
  eleventyConfig.addPlugin(baseline(settings, {
      dates: {
          datePublished: ['pubDate', ':default']
      }
  }));
  ```

  A plain name is a front-matter key. The derived sources are `:filename`, `:git-created`, `:git-modified` and `:datePublished`. Filesystem modification time is not a source and Baseline refuses it by name, for the reason the first breaking note gives.

  The git sources read one `git log` walk, cached once per build, and take the **author** date rather than the committer date, so rebasing does not restamp a page. They are skipped entirely outside a repository and on a shallow clone, where every file reports the same commit, and Baseline says one line about it rather than silently resolving nothing. GitHub Actions clones at depth 1 by default: set `fetch-depth: 0` if you want them. Neither line appears unless your source lists name a git token.

- **An asset index, exposed as `_assets`.** Each CSS and JS entry point is compiled once at the start of the build and recorded under the path you would write for it. A template can read the url, hash, integrity value and size:

  {% raw %}

  ```njk
  <script
      src="{{ _assets['/assets/js/index.js'].url }}"
      integrity="{{ _assets['/assets/js/index.js'].integrity }}"
      crossorigin="anonymous"
      defer
  ></script>
  ```

  {% endraw %}

  `url` is the fingerprinted path, which is what you want in a tag. `hash` is the content hash on its own, `size` is the byte count, and `integrity` is ready to drop into an SRI attribute.

- **Named entry points, with `postcss.entryPoints` and `esbuild.entryPoints`.** Each processor's own options bag, rather than a new key beside them.

  ```js
  assets: {
      postcss: { entryPoints: ['main.css'] },
      esbuild: { entryPoints: ['index.js', 'vendor/widget.js'] }
  }
  ```

  Paths are relative to that source's own directory, so `'vendor/widget.js'` rather than `'assets/js/vendor/widget.js'`. A declared list replaces the `index.css` and `index.js` convention for that processor instead of adding to it, so what you write is exactly what runs.

- **An `asset` filter**, for a template that names a file directly rather than through `settings.head`:

  {% raw %}

  ```njk
  <link rel="stylesheet" href="{{ '/assets/css/index.css' | asset }}">
  ```

  {% endraw %}

  It takes the path you wrote and returns the one the build produced. Most pages do not need it, because the head module already resolves what settings declare, and keeping asset paths in one place is the better habit.

- **A page-break marker above all of your content names the first part.** A marker names the part that follows it, and nothing follows nothing, so part one could only ever report its number. Write a marker at the top and it names part one without cutting anything, because the piece it would cut is empty.

  No part is added and no URL moves. Two sites had already worked around this independently, one hardcoding a label in its layout and one inventing a front-matter key.

- **`dir.cache` moves Baseline's cache.** Both the content graph cache and the media cache are anchored beside your input directory rather than resolved against the working directory, and `dir.cache` in your returned config moves them together. The default is unchanged at `.cache`.

---

## Fixed

- **A sitemap emits the `changefreq` and `priority` you authored.** It never has. The module wrote its computed entry to one path and its template read another, so unless you authored `lastmod` in front matter, three of the four values were dropped on the floor. `lastmod` could not be overridden either.

  - _Symptom: `<changefreq>` and `<priority>` elements appear for pages whose front matter declares them. If you have been setting them and seeing nothing, that is why._

- **`<lastmod>` comes from the resolved modified date.** Which is the last commit to touch the file unless your front matter says otherwise, rather than a file timestamp that moves on every deploy. A page with no resolvable date gets no `<lastmod>` element rather than a wrong one.

- **A language's own `lastmod` is emitted as written.** If you set `lastmod` on an entry in `settings.languages`, the sitemap index formatted it into a full timestamp. It is resolved like any other date now, so a calendar date stays the day.

  - _Symptom: a shorter `<lastmod>` in `/sitemap.xml` on a multilingual site that sets this. Almost nobody does._

- **A computed `schema.pieces` reaches the graph.** The documented extension seam did nothing on any page without pagination, which is most pages of most sites.

  Eleventy orders computed keys by what each function is seen to read. Baseline's returned early before reading anything, so it was scheduled ahead of the values it consumes. It now declares them. The same applies to a computed `ogImage` and `seo.ogImage`.

  - _Symptom: schema nodes you wrote in a computed function start appearing in your JSON-LD. If you moved them to static data to work around this, you can move them back._

- **A declared asset is checked against the file the build wrote.** `settings.head` entries naming a stylesheet or script were tested against the name you declared, while the build writes the fingerprinted one, so every entry point was reported as a missing file.

  - _Symptom: one spurious `declares ... which the build did not write` warning per declared entry point, gone. Three of them on this site._

- **A date Baseline cannot read is one warning, not six.** Three separate resolvers read the same page's dates, and Eleventy runs a computed function twice, once to work out what it reads and once for real, so a single bad value produced six identical lines.

  One of them owns the message now, and repeats are dropped. The message itself is unchanged: it names the page, the key and the value it could not read.

  - _Symptom: a quieter build if you have an unreadable date. Nothing to do._

- **The head module's count is the number of pages it wrote.** It counted input files, so a paginated template or a split page counted once however many pages it produced. On this documentation site the line read 85 against 103 files on disk, and had been wrong by eighteen for about two months.

  - _Symptom: a bigger number in `Head injected into N pages`. Nothing about your output changes._

---

## Internal

- The pre-pass re-entry guard asks the Eleventy config object whether it is the nested build, through a registry scope, rather than reading a process environment variable. Two builds in one process could read each other's, and an `eleventy.before` handler that checked the variable raced its own siblings, which left the asset index unbuilt in both passes on an otherwise green build.

  `BASELINE_PREPASS_ACTIVE` is still published and still set, because documented recipes read it. Nothing inside Baseline reads it any more.

- The navigator's debug page no longer renders during the content-graph pre-pass, which saves 161ms of a 2.4s pre-pass here. It is a debug surface with nothing the graph extracts.

- The collection-built translation map moved out of a closure into its own module, which is what made it testable. The two producers of a page's translation siblings key on different things, and the extraction is a guard against that mattering later rather than a fix for anything live.

- The dates substrate is new at `core/dates/`, with the `page.date` fallback chain deleted. One resolver answers for the JSON-LD graph, the Open Graph tags, the sitemap, the schemamap, the page context and the content graph, so no two surfaces can disagree about when a page was published.

- The `registerGlobals` aggregator is gone from the surface barrel, the `date` global having been its only member.

---

## Docs

- [[dates | Dates]] is a new reference page: why the substrate exists, the date-versus-instant model, the source lists, the time zone, git as a source, and where the resolved dates surface.
- The core reference chapter gained rows for `settings.timeZone`, `options.dates`, and the asset index globals.
