facebook / docusaurus

Easy to maintain open source documentation websites.
https://docusaurus.io
MIT License
55.79k stars 8.35k forks source link

RFC: new frontMatter to assign custom data to a doc #6923

Open slorber opened 2 years ago

slorber commented 2 years ago

Have you read the Contributing Guidelines on issues?

Motivation

For some documentation use-cases, it is convenient to be able to assign additional data to a doc

It should be possible to create fancy views/tables/aggregations of a set of documents based on those extra data and display those anywhere thanks to MDX.


This has been requested/discussed in https://github.com/facebook/docusaurus/issues/6302#issuecomment-1056869348

For example, @Danielku15 wants to display a table of a subset of the documents in a page of his documentation:

https://alphatab.net/docs/reference/settings/

image

When a doc is created, Docusaurus creates data for this doc in 3 places:

For data shared between multiple pages, the smallest the better. For performance reasons, we can't pre-emptively add any doc data everywhere without penalizing the performance of all the pages.


My suggestion is to add 2 distinct frontMatter to give the ability to assign extra data to a doc on a case-by-case basis

global_custom_props: 
  a: b
  c: d
  e: ["f"]

version_custom_props: 
  x: {y: 1, z: 2}

This would allow each doc to enrich the predefined minimal version/global data that Docusaurus needs to be able to run.

These data would become available in the React components (swizzled theme components or MDX embedded) so that users can create more fancy experiences.


What if you want to inject data that is not convenient to declare through yaml/frontMatter?

I think the createFrontMatter() proposal https://github.com/facebook/docusaurus/issues/5568 (also frontMatterParser PR https://github.com/facebook/docusaurus/pull/5972) could handle that:

global_custom_props: 
  dataPath: ./data/docX_global.json

And the createFrontMatter callback would be responsible to transform the path to a JS object.

And if I want to inject data that already exists as frontMatter?

That would be a bit odd to have to do this to propagate doc title in global data

title: my title
global_custom_props: 
  title: my title

Similarly using something like to createFrontMatter would help to set up propagation of existing frontmatter like title into global data

Self-service

Josh-Cena commented 2 years ago

This is basically the same as #4492, correct?

slorber commented 2 years ago

That looks quite similar yes, with a more concrete proposal and flexible API?

@kaytwo @zhouzi any opinion on this: would it solve your use-cases?

Danielku15 commented 2 years ago

Some remarks from my side on the proposal 😁

  1. [Clarification] Each Page should still have it's own individual set of properties it can expose globally or within the same version. Very simplified:
// docs/api/PropertyA.mdx
version_custom_props:
  available_since: 1.3.0
// docs/api/PropertyB.mdx
version_custom_props: 
  available_since: 1.2.0 
// docs/guides/SomeGuide.mdx
export function AvailableSince({id}) { return useDocById(id).version_custom_props.available_since; }

PropertyA is available since version <AvailableSince id="docs/api/PropertyA" /> while PropertyB is only already available since <AvailableSince id="docs/api/PropertyB" />

// docs/guides/SomeGuide.mdx rendered 

PropertyA is available since version 1.3.0 while PropertyB is only already available since 1.2.0

I personally would have no use case where all pages are writing to the same global object and can potentially overwrite the values. It would make things complicated to handle.

  1. [Clarification] Should we already try to define how we gonna access this data? It might influence the feature design, especially as you have performance concerns. 😉 From our earlier discussion I guess one way would be through useDocById() which would then have version_custom_props and global_custom_props members in the returned object? It might also be useful to access an overall object where the top level key is the docs id: { "docs/api/PropertyA": { /* same result as returned by getDocById? */ }, "docs/api/PropertyB": { ... }.

  2. [Example Usage] The second feature I would like to achieve with this enhancement is to add rich cross reference tooltips as we can see them when hovering links here in GitHub. When I'm referencing any setting, method, property documented on a different page, I want to provide a corresponding badge which has a tooltip revealing a sneak-peek to the information of the other page. The tooltip will be filled with data from the new title, description and the new **_custom_props:

zhouzi commented 2 years ago

@kaytwo @zhouzi any opinion on this: would it solve your use-cases?

Actually my use case is closer to #4138 than #4492 so I don't think this new feature would help with that. But it definitely looks like what's described in #4492 👍

Josh-Cena commented 2 years ago

4492 is recommended to be solved with #4138 because we'll likely not add such an option to propagate "arbitrary" front matter as globals. That sounds too prone to anti-patterns. This new custom_metadata front matter + createFrontMatter callback seems both more flexible and less "artificial"

PoetaKodu commented 2 years ago

I'm creating a "See also" section for C++ standard library symbols and could also make use of custom metadata. This would allow for the tables to be synchronized because right now we can either

The first one creates a lot of redundancy in the site source and is time-consuming. The second option uses the webpage's description which is not exactly what we want. Consider this example. We want a std::string::operator[] page to reference std::string::at() method providing its description.

In my case, the page description and the description for a see also section differ.

Wrong result: see_also

Site metadata: metadata

Danielku15 commented 1 year ago

Edit I just noticed that useDocById is still exported if you use import { useDocById } from '@docusaurus/theme-common/internal'; but unfortunately in TypeScript VS complains about missing type declarations: image

It compiles and runs though.

Original Post I planned to upgrade 2.0.0-beta.17 to 2.1.0 but useDocById seems to be unavailable from the official API. So actually parts of my feature request which was solved in #6302 did not make it to the official APIs or was accidentally reverted in https://github.com/facebook/docusaurus/commit/9473508c33132ac3c0ad7446ccd94fb7b386b085#diff-1ca3eb876a1746df7df2189265d991250dcbe1e54a9f23276d27b22da8b4d0bdL51

I need a mechanism to access also the description of a page by a given page ID to render it in my page. What's the officially intended way of doing this when getting the pages through currentSidebarCategory() and then wanting to access the description of those pages? Duplicating the description into a custom prop is not maintainable. DocCard seems to be the only internal usage of useDocById.

Ping @slorber and @Josh-Cena as authors behind the commit which has hidden useDocById.

slorber commented 1 year ago

@Danielku15 TS not being able to see this API is another issue, but it exists at runtime, and you can solve the TS problem with this workaround: https://github.com/facebook/docusaurus/issues/8226#issuecomment-1284255963

Note that useDocById is not the implementation that gives you access to all docs details, and it can only be called on docs pages of the same version, but it should give you access to the description.

Danielku15 commented 1 year ago

Thanks for the hint with the module resolution, will give that a try. Interesting to see that this is a common issue, I might have to check for my own lib if the different exports are handled correctly when consuming my library. 😅

I am aware that useDocById is not the full solution to this issue here, I also just use it for accessing the description of the same version which is fine for me. I am using useCurrentSidebarCategory for accessing the meta. Might have hijacked this issue because it is a bit the successor of my initial request.

ashleykolodziej-moodys commented 1 year ago

Hey there! I just wanted to comment that I like the idea of this implementation, and I think it would work for the use case I'm trying to achieve. I have a website that tracks design system elements running on Docusaurus, and I would like to be able to create a list of all docs with a badge next to each that represents the status of the component or feature - such as in design, in development, stable - in a table so that it's easy for someone to see at a glance the progress of the design system in real time. I have "system_status" as custom frontmatter, and putting it under something like version_custom_props would be easy for me to do across the board.

slorber commented 1 year ago

It is possible that React Server Components might be a better solution than the API I'm proposing in this RFC: https://github.com/facebook/docusaurus/issues/9089

We'll need to figure this out. Should we implement both or will RSC be enough to solve the reported use-cases?