Skip to content

feat: Taxonomy support#747

Open
tenhobi wants to merge 9 commits intoschultek:mainfrom
tenhobi:taxonomy
Open

feat: Taxonomy support#747
tenhobi wants to merge 9 commits intoschultek:mainfrom
tenhobi:taxonomy

Conversation

@tenhobi
Copy link
Copy Markdown
Contributor

@tenhobi tenhobi commented Feb 12, 2026

Fixes #746, Fixes #749

Description

Adds support for taxonomy including taxonomy loader and context helper functions.

Type of Change

  • ✨ New feature or improvement

Ready Checklist

  • I've read the Contribution Guide.
  • In case this PR changes one of the core packages, I've modified the respective CHANGELOG.md file using
    the semantic_changelog format.
  • I updated/added relevant documentation (doc comments with ///).
  • I added myself to the AUTHORS file (optional, if you want to).

@tenhobi tenhobi requested a review from schultek as a code owner February 12, 2026 18:02
@docs-page
Copy link
Copy Markdown

docs-page bot commented Feb 12, 2026

To view this pull requests documentation preview, visit the following URL:

docs.page/schultek/jaspr~747

Documentation is deployed and generated using docs.page.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 12, 2026

Package Version Report

The following packages have been updated:
jaspr_content : 0.5.0 -> 0.6.0

Copy link
Copy Markdown
Owner

@schultek schultek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the concept.

The main thing I'm sceptical about is using a new page loader for this, mainly because it's duplicating file io operations. It would be cool if it could operate on existing pages loaded by other page loaders, because then it could also work with the Github loader and potentially others.

Currently this isn't possible, because it would need to run after other page loaders and still be able to add pages itself, but I'm open to changing this.

@tenhobi
Copy link
Copy Markdown
Contributor Author

tenhobi commented Feb 13, 2026

That is a good point -- currently it only works on content files, not considering loaded pages at all. So having the ability for having "loaders" that can use pages from other loaders would be nice, even for other features.

I am not sure how loaders work right now - do they run in paralel, in sequence, etc?

Maybe also just having another list of "second run loaders" that could use pages loaded from regular loaders would just work. But then again, maybe you will want to process these generated pages from second run? But maybe this is stretching it too much and second run of loaders with access to first run pages would be completely fine?

@schultek
Copy link
Copy Markdown
Owner

All loaders are run in parallel, return a list of routes, and then awaited by the ContentApp.

So adding a step after this that is allowed to read pages and add new routes sounds reasonable. But I would restrict that to Routes, not Pages. The concept of pages requires some sort of content source, which doesn't exist for dynamically generated routes (Yes one could use MemoryPage but that feels like a workaround). Then we also don't have the problem of "processing pages from the second run".

Do you think that would be ok for the expected use-cases?

@tenhobi tenhobi requested a review from schultek February 13, 2026 16:01
Comment on lines +141 to +143
if (_eager && loadFutures.isNotEmpty) {
await Future.wait(loadFutures);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to do this for eacher loading, otherwise I was not able to wait for loads before running aggregators.

Copy link
Copy Markdown
Owner

@schultek schultek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing the changes.

My main thinking is still that aggregators should not produce pages, just routes, and therefore also not modify the pages list. And also not be a subclass of RouteLoader since it's output is not based on source files but on other pages.

I would also like to have it implemented in a way that aggregators are re-evaluated when any of the input pages change. The FilesystemLoader already watches the filesystem and invalidates + rebuilds a page when its source file changes. We should be able to hook into this.

Maybe we make the RouteLoader._pages itself something like a notifier, that others like the aggregators can listen to, and any operation on it (adding, removing pages) notifies the listeners.

The logic of aggregators subscribing to this and re-evaluating themselves can be done in a PagesAggregatorBase class (similar to the RouteLoaderBase).

this.routerBuilder = _defaultRouterBuilder,
this.aggregators = const [],
}) {
_overrideGlobalOptions();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add an assert here and in the other constructor that eagerlyLoadAllPages must be true when using aggregators. Wouldn't make sense otherwise.

Comment thread packages/jaspr_content/lib/src/content_app.dart Outdated
Comment thread packages/jaspr_content/lib/src/content_app.dart Outdated
///
/// ! This is primarily used by aggregators to analyze all pages.
@protected
static List<Page> get pages => _pages;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to expose this. Instead, the list should be given to an aggregator as a parameter.

///
/// Aggregators analyze loaded pages and produce additional pages/routes.
/// Implement [aggregate] to return pages that should be added to the site.
abstract class RoutesAggregator extends RouteLoaderBase {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't extend RouteLoader for the reason that I don't think aggregators should produce pages, just direct routes.

Aggregators should have a single abstract method Future<List<Route>> aggregatePages(List<Page> pages) to be implemented by any subclass. This then should be called by ContentApp and provided the RouteLoader._pages list (we need to find a way to access this from ContentApp without making it public).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also would call it PagesAggregator perhaps.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is because I don't have strong understanding of terms "route" and "page" in Jaspr. What is meant by "route" and what by "page"? Route is "url with builder", page is "content to display, with path, url and data" - or also specifically from content (file, memory, GitHub)?

I maybe don't see why producing this similar to what MemoryLoader does is a bad thing to do.

But we can for sure not use Page and loaders, and instead create our own structures for data where needed. In context of taxonomies it would be i.e. some inherited component that would provide the route component's way to access its own data (taxonomy, term) - so we can later look up all taxonomy/term routes that fit specified taxonomy or term. But also this will drop all access to context.pages, so there will have to be some way to access context.pages from term/taxonomy routes, no? So I can link them in /tags and /tags/flutter etc. routes. Also site data would not be there. And this is all we loose by not using already defined Page and memory loader for it and will have to be recreated in every aggregator by its own.

It would also mean I probably cannot react with other aggreggators to previously created routes/pages from other aggregators. But maybe that can be done by passing data to parameters.


tldr

  • in content pages I need to be able to lookup all taxonomy/term routes and its data (url, taxonomy, term)
  • in taxonomy/term routes I need to be able to lookup all pages (context.pages) so I can link to them from there

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have this conversation on the issue first before doing any more implementation.

@tenhobi
Copy link
Copy Markdown
Contributor Author

tenhobi commented Mar 9, 2026

Hi @schultek . I did another update to this. I checked it on my project that i works. Have a look please :)

Summary of Changes:

  • PagesAggregator is now a standalone abstraction separate from RouteLoader — receives loaded
    content pages and returns List<Route>
  • Added AggregatedRouteInfo as a base class for route metadata, accessible via
    context.currentRouteInfo<T>() inside route builders
  • InheritedAggregatedContext injects content pages and route infos into the entire component tree —
    aggregator routes have access to context.pages
  • TaxonomyAggregator moved to pagesAggregators (not loaders), produces TaxonomyRouteInfo /
    TaxonomyTermRouteInfo instead of Page objects
  • TaxonomyContext extensions (taxonomyTermRefs, taxonomyTermRef, taxonomyIndexRef,
    pagesForTerm, taxonomyTermRefsWithCount) work with TaxonomyTermRouteInfo instead of the old
    Page-based methods

@tenhobi
Copy link
Copy Markdown
Contributor Author

tenhobi commented Mar 23, 2026

Hi @schultek, I am just reminding. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: PageAggregator: analyze and emit new pages feat: taxonomy support

2 participants