Skip to content

WIP: Project state in plugins#2979

Open
rowanc1 wants to merge 1 commit into
mainfrom
wip-project-state-in-plugins
Open

WIP: Project state in plugins#2979
rowanc1 wants to merge 1 commit into
mainfrom
wip-project-state-in-plugins

Conversation

@rowanc1

@rowanc1 rowanc1 commented Jun 26, 2026

Copy link
Copy Markdown
Member

Working in person with @stefanv on blog listing through improving the global project state that is available to plugins.

image

This puts more information in the global plugin (document phase is most reliable), and can be exposed through plugins in a bit of a simpler way. This can help support blog listings, and many other full-project views of parsed content that do not rely on parsing the md files independently.


@changeset-bot

changeset-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d5082ab

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@rowanc1 rowanc1 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comments!

Comment on lines +2 to +11
version: 1
project:
title: Tagged Pages Plugin Example
plugins:
- plugin.mjs
toc:
- file: index.md
- file: apples.md
- file: oranges.md
- file: bananas.md

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not sure if this is worth putting in here, but sometimes it is nice to jump into a specific example?

project?: SessionProject;
};

export type PluginUtils = { select: Select; selectAll: SelectAll; unstableSession: SessionAPI };

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the main change, that we pass more information to the plugins.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

so if I understand, PluginUtils now gets an extra SessionAPI object, and within that object, plugin authors can access the SessionProject, and within that they can access the SessionSite and list of SessionPage objects?

* transforms (running in `postProcessMdast`) all pages have been processed so this
* data is complete.
*/
function getSessionProject(session: ISession, projectPath?: string): SessionProject | undefined {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Here we actually construct this. This function is pretty messy, and the goal is to resolve a clean API to consumers through the plugins.

@choldgraf choldgraf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems like a really nice start, and this would be a really useful addition for plugin authors. I linked #1616 , and added a few comments throughout.

In general, I think this looks pretty good - we should add a little bit of plugin dev docs so authors can learn more about this, and there were a few pieces that I thought would be particularly useful to add, though not critical as long as we don't close a door on them. If we don't have bandwidth for that right now, my vote would be to just get this in so we can experiment with it.

project?: SessionProject;
};

export type PluginUtils = { select: Select; selectAll: SelectAll; unstableSession: SessionAPI };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

so if I understand, PluginUtils now gets an extra SessionAPI object, and within that object, plugin authors can access the SessionProject, and within that they can access the SessionSite and list of SessionPage objects?

Comment on lines 296 to +297
// Load custom transform plugins
const documentSessionProject = getSessionProject(session, projectPath);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Load custom transform plugins
const documentSessionProject = getSessionProject(session, projectPath);
/** Document transform stage */
// Load custom transform plugins
const documentSessionProject = getSessionProject(session, projectPath);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just to make it clear this is document stage (I think it is?)

Comment on lines +461 to 462
const projectSessionProject = getSessionProject(session, projectPath);
session.plugins?.transforms.forEach((t) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const projectSessionProject = getSessionProject(session, projectPath);
session.plugins?.transforms.forEach((t) => {
/** Project transform stage (document stage is above) */
const projectSessionProject = getSessionProject(session, projectPath);
session.plugins?.transforms.forEach((t) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

also not critical - just trying to make the macro-structure of this a bit easier to parse

pipe.use(t.plugin, undefined, {
select,
selectAll,
unstableSession: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Quick check: if old plugins don't include unstableSession will they break until they update? Could we ensure it's optional so this doesn't happen?

*/
function taggedTransform(opts, utils) {
return async (mdast) => {
const { selectAll, unstableSession } = utils;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

unstableSession feels a little idiosyncratic to me - could we try to think of a more intuitive naming for plugin authors? Based on what's inside, my brain immediately went to something like sessionContext or buildContext or something like that...I feel like the unstable could make sense, but is also a bit verbose and may not be necessary as long as we document this functionality clearly enough (ie, note that it is unstable, and that certain moments in the build chain are more unstable than others)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do like the explicitness of "unstable". We could also use a namespace, e.g.

utils.hazmat.session.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I won't die on this hill if y'all like unstableFoo :-)

* affiliations, contributors, doi, etc.) extended with the page's location
* within the site.
*/
export type SessionPage = PageFrontmatter & {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this open a pathway towards us getting the AST from the page as well? I know that here it's just frontmatter, but I think it'd be quite useful to get information in the AST from other pages. For example, that "collect abbreviations" PR that @haroldcampbell opened up (#2985 ).

template?: string;
};

/** Minimal project information exposed to plugins via the unstable session API. */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why not pass the whole project config? I've seen several plugins want to have access to other config in myst.yml (though, in many cases they are defining their own config in myst.yml which might get dropped when it passes through the validator...).

I think this isn't strictly needed for this PR but want to flag it in case it is a design decision to leave open a pathway for doing this in a follow up

unstableSession: {
page: { slug: pageSlug, frontmatter },
project: documentSessionProject,
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Or if it's not easy to provide the AST to transforms, could we add parseMyst function to the transforms and then they could parse stuff themselves? (I think this would be useful either way)

Comment on lines +150 to +153
slug?: string;
index?: string;
title?: string;
pages: SessionPage[];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this should be one pages[] at the top. The slug/index/title should be the first one in here.

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Allow plugins access to the content and settings of all project documents via post-build event hooks

3 participants