11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.09k stars 494 forks source link

Add support for directory-based collections #2384

Open j-f1 opened 2 years ago

j-f1 commented 2 years ago

Is your feature request related to a problem? Please describe. I have a bunch of pages categorized by year and season on my website. I then want to display all the files for a given season together on a single index page for each season.

Describe the solution you'd like I would like a collection to be automatically created for each folder and subfolder in the input folder, and to have some sort of global data value listing all of those collections.

Maybe something like collections["folder:scripts"], collections["folder:scripts/2020-2021"], and collections["folder:scripts/2020-2021/fall"], plus a global folders that has a structure like this?

{
  scripts: {
    "2020-2021": {
      "fall": {}
    }
  }
}

…but I’m not super happy with that approach and I’m sure someone else can come up with a better one.

Describe alternatives you've considered

nachtfunke commented 2 years ago

I think this functionality would be great! I also have a scenario that would greatly benefit from this:

I am using Jérôme Coupé's way of translating content, this means that I am using permalink to write my websites in directories for each language respectively. So my websites are all in /en/, /de/, etc. In this project, I also had faq's that where language specific, meaning that some faq's where only visible in one language but not in the other. My initial idea was, to work with this structure:

en/
- faq/
- - A.md
- - B.md
- - C.md
- - faq.11tydata.js
- index.html
- en.11tydata.js
de/
- faq/
- - A.md
- - B.md
- - C.md
- - faq.11tydata.js
- index.html
- de.11tydata.js

But I realised that all of these pieces of content needed to be their own collection in order for me to access it as such. If they shared the tag faq, I instead needed to find a way to filter them. Which created another complication, because populating tags with eleventComputed is not possible.

The information about what faq's belong to where sits in eleventyComputed data, or in the directory structure. I found solutions to this issue thanks to @j-f1 's help in the 11ty Discord, but it would have been very intuitive, if we had something like local collections, based on folder structures.

pdehaan commented 2 years ago

I imagine this might be difficult to do in a way that works for everybody's specific file/directory structure. I did make a rough attempt at an auto-collection creator, but it doesn't do as deeply nested as your example: https://github.com/pdehaan/11ty-glob-coll

But given a directory structure like:

src
├── blog/
│   ├── 2020/
│   │   └── one.md
│   ├── 2021/
│   │   ├── four.md
│   │   └── two.md
│   ├── 2022/
│   │   └── three.md
│   └── tips/
│       └── addplugin.md
└── index.liquid

It will try and make collections named "blog_2020", "blog_2021", "blog_2022", and "blog_tips" directories and create custom collections based on a rough "./src/blog/*/*.md" style glob. I don't have a huge number of directories so it would have probably been easier/cleaner to just add directory data files like "src/blog/2020/2020.json" (with a tags entry) and let Eleventy manage my collections, but might be trickier given a larger number of folders.

Not sure of an easy way to use eleventyComputed to dynamically set the tag name, and then potentially have some other script that creates custom collections based on those dynamically set tags (since dynamically set tags don't automatically create collections).

The repo also has a pagination file which will create www/blog/2020/index.html files with a listing of all the posts in that collection. You can find the custom plugin at auto_collection_plugin.js and the config in the .eleventy.js file.

jeromecoupe commented 2 years ago

@nachtfunke I usually work with folder based collections for this king of setup using getFilteredByGlob instead of tags. With your FAQ use case, that would give you access to one FAQ collection per language that could contain different FAQ items. That being said, it does not solve the problem of collections based on deeply nested folder structure.

jeromecoupe commented 2 years ago

@j-f1 Not sure if that would fit your exact use case, but I think my current take on this would probably be to build a scripts collection containing every script using getFilteredByGlob. I would then create custom filters (getScriptsByYear, getScriptsBySeason) that would filter that collection based on certain parameters (keys) available in the front-matter, like season or year, or both. That means you would have to move season and years in the front matter of those files, though.

EDIT: coming to think of it, maybe you could use the inputPath to filter things out to rely on your folder structure rather than on front-matter values.

If you would like to paginate those filtered collection items, then you need another approach, which involves creating chunked collections. Here is a (somewhat old) repo using that approach.

patrulea commented 1 year ago

I did make a rough attempt at an auto-collection creator, but it doesn't do as deeply nested as your example: https://github.com/pdehaan/11ty-glob-coll

Thank you @pdehaan 👏
This should be in the docs—too useful!

nhoizey commented 1 year ago

Automatically creating collections based on folders is a feature of Pack11ty, my Eleventy starter: https://pack11ty.dev/documentation/collections/#auto-collections

I've been moving most features of Pack11ty into a plugin a few days ago, so that it can be added to any existing site: https://github.com/nhoizey/eleventy-plugin-pack11ty/

(Documentation is not yet fully up-to-date.)

If you have a collections/ sub-folder in your source folder, each of its sub-folders becomes a collection. For example, there will be a news collection with all content from collections/news/**/*.

There are also automated collections for archives like yearsWithNews, newsByYear, newsByMonth.