Open querkmachine opened 9 months ago
Hey @querkmachine, I think there are two already working solutions for your problem (if I understand it correctly that you want to access the eleventyConfig object from inside your filters). Both are related.
First: Wrap them in a plugin To do this, you can write your filters in ./config/filters/cool-ranking.js as:
module.exports = (eleventyConfig) => {
eleventyConfig.addFilter("myCoolFilter", () => {/*...*/});
eleventyConfig.addFilter("myLessCoolFilter", () => {/*...*/});
}
And in your normal eleventy config you'd do:
const filters = require("./config/filters/cool-ranking.js");
module.exports = function (eleventyConfig) {
// ...
eleventyConfig.addPlugin(filters);
// ...
};
Or second: Use higher order functions. Higher order functions are often used for plugin systems. To do this you wrap your filter functions in a function that returns the filter function. So your ./config/filters/cool-ranking.js would be:
const myCoolFilter = (eleventyConfig) => {
return (filterParam) => {
// Your filter impl. using eleventyConfig
}
}
module.exports = {
myCoolFilter,
//...
}
And then you can use it via:
const { myCoolFilter, myLessCoolFilter } = require("./config/filters/cool-ranking.js");
module.exports = function (eleventyConfig) {
// ...
eleventyConfig.addFilter("myCoolFilter", myCoolFilter(eleventyConfig));
// ...
};
Recommendation I personally prefer the first option over the second, because it's more concise and is closer to an "intended" way of doing things.
Also, in the context of eleventyComputed
, as long as you aren't using arrow functions, the filters should be accessible in the this.
scope:
module.exports = {
eleventyComputed: {
opengraphImageUrl(data) {
console.log({
"this": this,
page: data.page,
});
return this.url(`/images/opengraph/${data.page.fileSlug}.png`);
},
},
};
Wouldn't solve all your use cases, but big 👍 to everything that @Snapstromegon pointed out above.
Thank you for the answers, both! I'll probably follow the recommendation of bundling things together into plugins.
I'm gonna leave this open as I still think there might be value in the means of accessing filters being more direct, or at least having these use cases be better documented on the website.
I would like to access the built-in slugify
filter from a data function, so I can't just require
it from the <data>.js
file as @Snapstromegon suggests in https://github.com/11ty/eleventy/issues/3114#issuecomment-1826369696. I can see that something which looks like a context object of some sort is passed into the data function:
// _data/some_data_file.js
module.exports = async function(context) {
console.log(context); // some sort of context object, not sure what use it has
console.log(this.eleventy); // undefined
// …
return data;
}
If I log this context
object to the console, it looks like the following:
{
eleventy: {
version: '2.0.1',
generator: 'Eleventy v2.0.1',
env: {
source: 'cli',
runMode: 'build',
config: '…/.eleventy.js',
root: '…',
isServerless: false
}
}
}
It doesn't seem like I can do anything useful with this object, and this.eleventy
is undefined
within the data function.
Also, I don't quite understand whether or how I can use the eleventyComputed
return object from my data function, as currently, it's returning an array of data objects. What I would like to do is use slugify
for every data object being returned, to create the URL of each data object centrally, avoiding having to build the URL with | slugify
everywhere in my templates.
Any suggestions and help would be highly appreciated.
@asbjornu What do you mean with "having to build the URL with | slugify
everywhere"?
I think you should only need it when setting the permalink of a page manually. When using the page's url, it should already be in a slugified form.
Maybe you could provide an example?
@Snapstromegon, I want to set the URL on the data objects returned from the data function, not on a page. These data objects are used in various places in my templates and I would like the URL to be set once in the data function before the objects are returned, so I don't have to repeat the URL building everywhere these data objects are used in my templates.
Here's an example:
// _data/some_data.js
module.exports = async function(context) {
const data = [
{ name: 'Lorem ipsum' },
{ name: 'Dolor sit' },
{ name: 'Amet, consectetur' }
];
for (const item of data) {
let slug = context.eleventy.slugify(item.name); // TypeError: context.eleventy.slugify is not a function
slug = this.eleventy.slugify(item.name); // Cannot read properties of undefined (reading 'slugify')
data.url = `/prefix/${slug}/`;
}
return data;
};
My use case seems related to #2790, but for data functions instead of template functions.
@asbjornu In your special case, you could also import the slugify filter directly via (esm syntax here, works the same for cjs):
import slugify from "@11ty/eleventy/src/Filters/Slugify.js";
But this is not a general solution. Maybe we could expose the UserConfig
object on the context.eleventy
object passed into a data function.
If I find some time, I will take a look at this.
If you put your filters wrapped in a plugin, as suggested by https://github.com/11ty/eleventy/issues/3114#issuecomment-1826369696 (and originally https://www.lenesaile.com/en/blog/organizing-the-eleventy-config-file/#method-3-adding-another-config-file-as-a-plugin), or just anywhere you have access to eleventyConfig
, you can access the eleventyConfig.javascriptFunctions
object to get the slugify
function as well:
eleventyConfig.javascriptFunctions["slugify"]("Hello, world!");
// "hello-world"
@uncenter I'd recommend using the eleventyConfig.getFilter() method instead, but yes, this also works.
Ah, good to know!
Is your feature request related to a problem? Please describe.
Eleventy 0.11.0 added the ability to use Eleventy's built-in filters from within the configuration file using the
eleventyConfig.getFilter
function.However, my projects can end up having a lot of custom filters, shortcodes and other bits-n-bobs. To keep things tidy, I place my custom functions definitions in their own files rather than directly within the config function.
For example, part of my configuration might look more like this:
The filter function not being inline within the file prevent me from accessing the
eleventyConfig
andgetFilter
from withincool-ranking.js
unless I manually pass it into the function, but this would seemingly make it difficult to use as a filter within templates.I've also encountered times where being able to access filters in
.11tydata.js
files would be useful, but there similarly doesn't seem to be a method of doing so, such as here:Describe the solution you'd like
I'm not sure how practical or easy it would be, especially as filters like
url
andget*CollectionItem
are at least somewhat dependent on the configuration itself, but it'd be nice if the built-in filters could be available in more contexts.Describe alternatives you've considered
At the moment I've resorted to reproducing the built-in features as custom functions, sometimes using the same dependencies as Eleventy itself, such as using the
@sindresorhus/slugify
package to duplicate theslugify
filter.This works in the short term, but there feels like there's a risk of the configurations being changed or falling out of sync over time, so it's not an ideal alternative in my mind.
Additional context
No response