eleventy icon indicating copy to clipboard operation
eleventy copied to clipboard

TemplateContentPrematureUseError resulting from eleventyConfig.addFilter()

Open andy-blum opened this issue 1 year ago • 2 comments

Operating system

mac OS Sequoia, Debian 6.1

Eleventy

3.0.0

Describe the bug

Build output errors with the following:

> eleventy

[11ty] Problem writing Eleventy templates:
[11ty] 1. Having trouble rendering njk template (via TemplateContentRenderError)
[11ty] 2. Tried to use templateContent too early (via TemplateContentPrematureUseError)
[11ty]
[11ty] Original error stack trace: TemplateContentPrematureUseError: Tried to use templateContent too early
[11ty]     at Object.get [as templateContent] (file:///var/www/html/node_modules/@11ty/eleventy/src/Template.js:620:14)
[11ty]     at Object.get [as content] (file:///var/www/html/node_modules/@11ty/eleventy/src/Template.js:640:18)
[11ty]     at /var/www/html/.eleventy.js:147:25
[11ty]     at Array.map (<anonymous>)
[11ty]     at lunr.Builder.<anonymous> (/var/www/html/.eleventy.js:142:10)
[11ty]     at lunr (/var/www/html/node_modules/lunr/lunr.js:53:10)
[11ty]     at Context.<anonymous> (/var/www/html/.eleventy.js:127:24)
[11ty]     at Context.<anonymous> (file:///var/www/html/node_modules/@11ty/eleventy/src/UserConfig.js:419:24)
[11ty]     at Context.fn (file:///var/www/html/node_modules/@11ty/eleventy/src/Benchmark/BenchmarkGroup.js:37:23)
[11ty]     at Context.<anonymous> (file:///var/www/html/node_modules/@11ty/eleventy/src/Engines/Nunjucks.js:109:15)
[11ty] Copied 2 Wrote 0 files in 0.28 seconds (v3.0.0)
Failed to run npm run build: exit status 1

The root cause appears to be trying to pre-build a search index for Lunr.js. To accomplish this, I've created a new filter using eleventyConfig.addFilter() which iterates over all the items in a collection, and calls the content getter.

I did not encounter this error during development. If I comment out this specific call to content and build, followed by un-commenting and building with --incremental everything seems to work ok. This is fine for local, but breaks testing & production deploys.

Reproduction steps

Currently experiencing this in https://github.com/Lullabot/architecture/pull/693

npm run build

Expected behavior

Running eleventy's build command would successfully build the site

Reproduction URL

No response

Screenshots

No response

andy-blum avatar Oct 12 '24 21:10 andy-blum

Was able to bypass the error by forcing the template to render all the content values in a way it expected, but that didn't actually print anything.

---
layout: false
permalink: /searchindex.json
eleventyExcludeFromCollections: true
---
{#
  Needed to prevent TemplateContentPrematureUseError.
  Doesn't actually get printed.
#}

{% set rendered %}
  {% for adr in collections.adrs %}
    {{ adr.content }}
  {% endfor %}
{% endset %}

{# This actually does what I want. #}
{% lunrIndex collections.adrs %}

andy-blum avatar Oct 15 '24 13:10 andy-blum

We ran into this issue too, but only on certain devices, and only intermittently; which suggests some sort of race condition.

Our use case was paginating through the list of collections to return a JSON of all posts per collection in a specific format:

export.njk
---js
{
	"sitemap": false,
	"eleventyExcludeFromCollections": true,

	"pagination": {
		"data": "collections",
		"size": 1,
		"alias": "collection",
		"addAllPagesToCollections": false
	},

	"eleventyComputed": {
		"permalink": function() {
			return process.env.NODE_ENV === 'production' ? false : 'export/{{ collection }}.json';
		}
	}
}
---
{{ collections[collection] | export | safe }}
export.js filter
/* Keys of recursive or unnecessary data supplied by Eleventy and data files */
const pollution = [
	/* Eleventy internals */
	'eleventy',
	'eleventyComputed',
	'collections',
	'pkg',
	'page',
	'pagination',
	/* etc */
];

export default function (collection) {
	/* Clean up the collection items */
	const sanitisedCollection = Array.isArray(collection) ? collection.map(item => {
		const data = { ...item.data };

		/* Remove unnecessary keys from data */
		for (const pollutant of pollution) {
			if (pollutant in data) {
				delete data[pollutant];
			}
		}

		/* Construct minimal page object */
		return {
			data,
			page: item.page,
			slug: item.fileSlug,
			url: item.url,
			content: item.content,
		};
	}) : [];

	return JSON.stringify(sanitisedCollection);
}

In our case, we only solved it by adding the entire list of collections into eleventyImport:

---js
{
	/* ... */

	"pagination": {
		"data": "collections",
		"size": 1,
		"alias": "collection",
		"addAllPagesToCollections": false
	},

	"eleventyImport": {
		"collections": [
			"posts",
			"pages",
			"things",
			"stuff",
			/* etc */
		]
	},

	/* ... */
}
---
{{ collections[collection] | export | safe }}

However, it's not very durable to maintain a manual list of all the collections in this specific file (and remembering to add and remove values as the collections change over time).

In the absence of any other fix for this, I'd at least be interested to see if there's a way to use the JS frontmatter to automatically generate the array of all available collections to use for eleventyImport.collections - or any other change I should make to my filter or pagination to avoid TemplateContentPrematureUseError errors.

groenroos avatar Oct 17 '24 10:10 groenroos

I don’t see https://github.com/Lullabot/architecture/pull/693 is it a private repo?

eleventyImport and eleventyExcludeFromCollections can be used to influence template rendering order

  • https://www.11ty.dev/docs/collections/#declare-your-collections-for-incremental-builds
  • https://www.11ty.dev/docs/collections/#how-to-exclude-content-from-collections

You can use them together, we use this in the RSS Virtual Template:

https://github.com/11ty/eleventy-plugin-rss/blob/76d452a847322d25c525d17dceb83eb07e9b9793/src/virtualTemplate.js#L138-L148

zachleat avatar Oct 21 '24 21:10 zachleat

Oh, sorry, yeah. I forgot that's a private repo.

The basic structure of what was happening is that I created a filter in .eleventy.js. This filter was applied to a collections.collectionName to iterate over each entry in that collection to generate a static search index for Lunr.js

This all worked fine locally when the dev server was already up and running, but a cold start failed with the TemplateContentPrematureUseError. Even with eleventyImport and eleventyExcludeFromCollections, a cold build wouldn't work.

I have since added a for loop in the template where I want the generated index that doesn't actually output anything. That seems to have sidestepped whatever bug was happening (see code in previous comment)

andy-blum avatar Oct 25 '24 20:10 andy-blum