-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: handle all admonitions via JSX component #7152
Conversation
✅ [V2]
To edit notification comments on pull requests, go to your Netlify site settings. |
⚡️ Lighthouse report for the deploy preview of this PR
|
Size Change: +50 B (0%) Total Size: 798 kB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for bringing this up!
I'm very much in favor of this, and I think everyone agrees this is what we would eventually do (see #6246). However, Sebastien and I talked about this on Discord some time in the past (IIRC), and I think the consensus was that we would only fork dependencies after we've had the @Docusaurus organization properly set up and ready to be populated with forks. I think we are in a pretty awkward situation right now, because we have neither the architecture nor sufficient incentive to make this change happen.
(config.module?.rules as webpack.RuleSetRule[])?.forEach((rule) => { | ||
if (Array.isArray(rule.use)) { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
rule?.use.forEach((useItem: any) => { | ||
if (useItem.loader!.includes('docusaurus-mdx-loader')) { | ||
useItem?.options.remarkPlugins.push(admonitions); | ||
} | ||
}); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks kind of hacky. In the future we can use #6370 to handle this (this is already marked as one of the use-cases of configureMarkdownLoader
).
Also, because the fallback MDX loader in the core is pushed after all plugins, I think this would make admonitions unavailable to documents using the fallback loader? Maybe we need to change that order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was the only way to push remark plugin into the all MDX loaders. Are admonitions in the fallback loader really necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it was added in core specifically because someone brought it up: #5333 it's mostly for partials not in the docs directory. It can be trivially fixed though, we just need to push the synthetic plugins before the others:
docusaurus/packages/docusaurus/src/server/plugins/index.ts
Lines 38 to 44 in 0abf4d7
// 1. Plugin Lifecycle - Initialization/Constructor. | |
const plugins: InitializedPlugin[] = await initPlugins(context); | |
plugins.push( | |
createBootstrapPlugin(context), | |
createMDXFallbackPlugin(context), | |
); |
I see, but on the other hand it is not that big dependency and not that significant a change after all, so I would suppose that at this moment we could adopt this solution after fix some issues with the code (in particular with types). |
Yeah, I will take a look at the types tomorrow, see what I can help to fix |
This is hard. Because the MDX fallback plugin would also search for all existing MDX loaders and only include those files that are not yet included, putting it before the content plugins will result in duplicate loaders and then a dead cycle. What about we move this remark plugin to the MDX loader? Since it's generic enough (only requires the existence of some MDX mapping from |
const element = { | ||
type: 'admonitionHTML', | ||
data: { | ||
hName: 'admonition', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a pretty good step. This also means users can swizzle MDXComponents
and re-map admonition
to some other component, not necessarily @theme/Admonition
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 totally in favor of this
) { | ||
useItem.options ??= {}; | ||
(useItem.options as MDXOptions).remarkPlugins ??= []; | ||
(useItem.options as MDXOptions).remarkPlugins.push(admonitions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to support custom admonitions as we previously did. We have users heavily using them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree 👍
can you show example sites using custom admonitions and the configs used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Josh-Cena I think we should also study how changes in this PR will look like after MDX 2 and https://github.com/remarkjs/remark-directive
I don't think the current API for admonition customization will stay as is, and instead we'll do customizations through swizzle?
So maybe we can already do the breaking change today if it's already planned anyway 🤷♂️
Thanks for the fixes!
Just the original idea was to keep all the related things together, i.e. the remark plugin, the |
You mean, |
Yes, something similar, but so far in a simplified form, enough to add that new remark plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks 👍
In favor of doing, clearly better for customizations to use our theme component consistently 👍
Will have to check the copyright thing
@@ -0,0 +1,136 @@ | |||
/** | |||
* Copyright (c) Facebook, Inc. and its affiliates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically a modified copy of MIT-based code from https://github.com/elviswolcott/remark-admonitions/blob/master/lib/index.js on which you put Facebook copyright and not mention previous copyright
Technically I'm fine "owning" this code in the Docusaurus codebase, but I'm not sure this can be done this way legally, will have to check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also we can probably publish this as a separate standalone package as it could be useful in other contexts.
We may eventually create a more generic tool that transforms custom syntax to JSX/MDX?
For example, we could also create a new custom syntax to handle tabs, similarly to this:
https://retype.com/components/tab/#multiple-tabs
(not so sure, maybe after we upgrade to mdx 2 we'll just need https://github.com/remarkjs/remark-directive)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we can likely drop a LICENSE file in a subfolder to preserve the original license
Example: https://github.com/zpao/qrcode.react/tree/main/src/third-party/qrcodegen
Is all code in this subfolder coming from the original repo? Otherwise we can split between new files + copied files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slorber It's not a verbatim copy (and in fact significantly refactored, if not a total rewrite), so we need to have the licenses of both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I know, trying to figure this out. The above copy example has also been modified btw.
const element = { | ||
type: 'admonitionHTML', | ||
data: { | ||
hName: 'admonition', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 totally in favor of this
) { | ||
useItem.options ??= {}; | ||
(useItem.options as MDXOptions).remarkPlugins ??= []; | ||
(useItem.options as MDXOptions).remarkPlugins.push(admonitions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree 👍
can you show example sites using custom admonitions and the configs used?
@@ -168,11 +167,5 @@ export function validateOptions({ | |||
|
|||
const normalizedOptions = validate(OptionsSchema, options) as PluginOptions; | |||
|
|||
if (normalizedOptions.admonitions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is removed, but admonitions remains in schema + default value
We want to still allow custom admonitions? Are custom admonitions configured globally or per plugin?
Do we want to do a breaking change and add a custom error message, or try to keep retro-compatibility?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep this here for the time being, but when we eventually have a centralized markdown.remarkPlugins
config (#5999) I'd like to move it there. Note that I also want to have admonitions
as part of the default MDX loader preset, so it can be configured as part of the docusaurus-remark-preset
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Josh-Cena I agree we should keep the options there, at least for now
The problem is that the provided options do not make sense anymore
{
admonitions: {
customTypes: {
homebrew: {
keyword: `homebrew`,
infima: true,
svg: '<svg /></svg>'
},
gamerchic: {
keyword: `gamerchic`,
infima: true,
svg: '<svg /></svg>'
},
},
},
}
Now the options should look like:
{
admonitions: {
customKeywords: ["homebrew","gamerchic"]
},
}
And custom styling/svg should be applied by swizzling the admonition component.
@lex111 it's worth supporting that to ensure that the above site config have a way to upgrade Docusaurus without losing its custom admonitions
We also need to add option schema changes so that it prints a clear error message when user is trying to use the "old" customizations format
), | ||
); | ||
|
||
config.module?.rules?.forEach((rule) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤪 not a fan of this
Maybe we could just add an mdx-loader "admonitions" option on which we'd just forward the plugin options (if we keep them?)
I know it's not ideal to add everything to mdx-loader but it's probably a good-enough temporary solution until we figure out something better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I explained above, the idea was for the theme to provide admonitions functionality, so the remark plugin should add from the theme. That's why I suggested to implement a simplified lifecycle hook to solve only this issue for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can make the loader provide an optional "admonitions" feature that the theme would enable
it's less flexible but would definitively work without needing any extra lifecycle, giving us time to figure out a good API for that lifecycle. If we can avoid introducing temporary experimental public apis that's better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can make the loader provide an optional "admonitions" feature that the theme would enable
Do you mean move the admonitions plugin to the mdx-loader or something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for now, you can move the admonition plugin to mdx loader and pass an admonition: {customKeywords: []}
option. We'll likely move it outside later but for now it's the simplest solution.
# Conflicts: # packages/docusaurus-plugin-content-blog/package.json # packages/docusaurus-plugin-content-blog/src/index.ts # packages/docusaurus-plugin-content-docs/package.json # packages/docusaurus-plugin-content-pages/package.json
Thanks for finalizing this PR, I just migrated the styles to CSS modules and also finally fixed the longstanding a11y problem related with headings. |
Yes, that's great! We can merge this as soon as we get confirmation about the licensing. |
GitHub supports the new admonition syntax: > **Note**
> a note
> A note...
Once we merge this, this can be easily implemented in user-land. |
|
||
let newValue = value; | ||
// consume lines until a closing tag | ||
let idx = newValue.indexOf(NEWLINE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this can be done in this PR or in a followup. But i would suggest to support nested Admonitions aswell, s.t. something like
:::info Weather
On nice days, you can enjoy swimming in the lake.
:::danger Storms
Take care of summerstorms...
:::
:::
would work as expected.
To do this, we would need to keep track of the nesting level. I already did something similar for elviswolcott/remark-admonitions#41 here: https://github.com/elviswolcott/remark-admonitions/pull/42/files#diff-92bbac9a308cd5fcf9db165841f2d90ce981baddcb2b1e26cfff170929af3bd1R170
Would be nice to see this supported here aswell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that info @lebalz Let's do that as a follow-up because this one is supposed to be a simple port.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a regression: https://deploy-preview-7152--docusaurus-2.netlify.app/docs/api/misc/@docusaurus/logger/
Markdown inside admonition title is no longer interpolated.
I don't know it's serious enough to block this PR, but want to bring that to people's attention. I deliberately included that text in the documentation because we do have users relying on it. See #7173
Another idea: this is a good time to make a breaking change. We should not have individual |
# Conflicts: # packages/docusaurus-plugin-content-pages/src/index.ts
Damn, really want to merge this one but couldn't find the solution yet 😅 |
After discussing with @wooorm it appears to be quite complex to support an MDX title like <admonition title={<>my <code>title</code></>}>
<p>body</p>
</admonition> This will be easier in MDX v2. In the meantime, I implemented a temporary workaround where everything goes inside <admonition>
<mdxAdmonitionTitle>my <code>title</code><mdxAdmonitionTitle/>
<p>body</p>
</admonition> function extractMDXAdmonitionTitle(children: ReactNode): {
mdxAdmonitionTitle: ReactNode | undefined;
rest: ReactNode;
} {
const items = React.Children.toArray(children);
const mdxAdmonitionTitle = items.find(
(item) =>
React.isValidElement(item) &&
(item.props as {mdxType: string} | null)?.mdxType ===
'mdxAdmonitionTitle',
);
const rest = <>{items.filter((item) => item !== mdxAdmonitionTitle)}</>;
return {
mdxAdmonitionTitle,
rest,
};
} This is not ideal but good enough to support this edge case and not block this PR Tests: https://deploy-preview-7152--docusaurus-2.netlify.app/docs/api/misc/@docusaurus/logger/ https://deploy-preview-7152--docusaurus-2.netlify.app/tests/pages/markdownPageTests#admonitions |
Will add that in a separate PR |
Looks good enough to me👍 This is what I figured out as well. |
The PR look good at a glance now, will try more stuff locally when I get to my keyboard |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some pending followup work to do, but this is good to go!
Breaking changes
The previously supported (but undocumented) admonition options are now working differently.
Should be refactored as:
Also the customization (SVG etc) should be handled with swizzle
Motivation
Attempting to handle all admonitions through the appropriate JSX component. This is not finished version, but POC, there are many issues with typing, but it's works. Since we have a separate JSX component for admonitions, and we even define our own styling for them as admonitions.css, it seems to make sense that the classic theme would be fully responsible for implementing the functionality of admonitions.
What I see as the benefits of this:
Technically, new remark plugin have added via
configureWebpack
hook in a rather non-immutable way, it might be worth creating a new hook to configure remark, something likeconfigureRemark
, not sure about that.Maybe I'm missing some flaws, but it seems like it should be better, if the admonitions will be included from the theme, considering high cohesion between them before.
Fix #7344
Fix #7478
Have you read the Contributing Guidelines on pull requests?
Yes
Test Plan
Preview: all admonitions should be displayed in the same way as before.
Related PRs