mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-03 08:49:51 +02:00
fix(core): do not apply theme-init alias to user component (#5983)
Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
b366ba5603
commit
fcaa94695d
11 changed files with 76 additions and 22 deletions
|
@ -14,7 +14,7 @@ describe('loadThemeAliases', () => {
|
|||
const theme1Path = path.join(fixtures, 'theme-1');
|
||||
const theme2Path = path.join(fixtures, 'theme-2');
|
||||
|
||||
const alias = loadThemeAliases([theme1Path, theme2Path]);
|
||||
const alias = loadThemeAliases([theme1Path, theme2Path], []);
|
||||
|
||||
// Testing entries, because order matters!
|
||||
expect(Object.entries(alias)).toEqual(
|
||||
|
|
|
@ -12,34 +12,31 @@ import themeAlias, {sortAliases} from './alias';
|
|||
|
||||
const ThemeFallbackDir = path.resolve(__dirname, '../../client/theme-fallback');
|
||||
|
||||
function buildThemeAliases(
|
||||
themeAliases: ThemeAliases,
|
||||
aliases: ThemeAliases = {},
|
||||
): ThemeAliases {
|
||||
Object.keys(themeAliases).forEach((aliasKey) => {
|
||||
if (aliasKey in aliases) {
|
||||
const componentName = aliasKey.substring(aliasKey.indexOf('/') + 1);
|
||||
aliases[`@theme-init/${componentName}`] = aliases[aliasKey];
|
||||
}
|
||||
aliases[aliasKey] = themeAliases[aliasKey];
|
||||
});
|
||||
return aliases;
|
||||
}
|
||||
|
||||
export function loadThemeAliases(
|
||||
themePaths: string[],
|
||||
userThemePaths: string[] = [],
|
||||
userThemePaths: string[],
|
||||
): ThemeAliases {
|
||||
let aliases = {}; // TODO refactor, inelegant side-effect
|
||||
const aliases: ThemeAliases = {};
|
||||
|
||||
themePaths.forEach((themePath) => {
|
||||
const themeAliases = themeAlias(themePath, true);
|
||||
aliases = {...aliases, ...buildThemeAliases(themeAliases, aliases)};
|
||||
Object.keys(themeAliases).forEach((aliasKey) => {
|
||||
// If this alias shadows a previous one, use @theme-init to preserve the initial one.
|
||||
// @theme-init is only applied once: to the initial theme that provided this component
|
||||
if (aliasKey in aliases) {
|
||||
const componentName = aliasKey.substring(aliasKey.indexOf('/') + 1);
|
||||
const initAlias = `@theme-init/${componentName}`;
|
||||
if (!(initAlias in aliases)) {
|
||||
aliases[initAlias] = aliases[aliasKey];
|
||||
}
|
||||
}
|
||||
aliases[aliasKey] = themeAliases[aliasKey];
|
||||
});
|
||||
});
|
||||
|
||||
userThemePaths.forEach((themePath) => {
|
||||
const userThemeAliases = themeAlias(themePath, false);
|
||||
aliases = {...aliases, ...buildThemeAliases(userThemeAliases, aliases)};
|
||||
Object.assign(aliases, userThemeAliases);
|
||||
});
|
||||
|
||||
return sortAliases(aliases);
|
||||
|
|
|
@ -23,13 +23,14 @@ Object {
|
|||
"@docusaurus/useIsBrowser": "../../../../client/exports/useIsBrowser.ts",
|
||||
"@generated": "../../../../../../..",
|
||||
"@site": "",
|
||||
"@theme-init/PluginThemeComponentOverridden": "pluginThemeFolder/PluginThemeComponentOverridden.js",
|
||||
"@theme-init/PluginThemeComponentEnhanced": "pluginThemeFolder/PluginThemeComponentEnhanced.js",
|
||||
"@theme-original/Error": "../../../../client/theme-fallback/Error/index.js",
|
||||
"@theme-original/Layout": "../../../../client/theme-fallback/Layout/index.js",
|
||||
"@theme-original/Loading": "../../../../client/theme-fallback/Loading/index.js",
|
||||
"@theme-original/NotFound": "../../../../client/theme-fallback/NotFound/index.js",
|
||||
"@theme-original/PluginThemeComponent1": "pluginThemeFolder/PluginThemeComponent1.js",
|
||||
"@theme-original/PluginThemeComponentOverridden": "pluginThemeFolder/PluginThemeComponentOverridden.js",
|
||||
"@theme-original/PluginThemeComponentEnhanced": "secondPluginThemeFolder/PluginThemeComponentEnhanced.js",
|
||||
"@theme-original/PluginThemeComponentOverriddenByUser": "pluginThemeFolder/PluginThemeComponentOverriddenByUser.js",
|
||||
"@theme-original/Root": "../../../../client/theme-fallback/Root/index.js",
|
||||
"@theme-original/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js",
|
||||
"@theme/Error": "../../../../client/theme-fallback/Error/index.js",
|
||||
|
@ -37,7 +38,8 @@ Object {
|
|||
"@theme/Loading": "../../../../client/theme-fallback/Loading/index.js",
|
||||
"@theme/NotFound": "../../../../client/theme-fallback/NotFound/index.js",
|
||||
"@theme/PluginThemeComponent1": "pluginThemeFolder/PluginThemeComponent1.js",
|
||||
"@theme/PluginThemeComponentOverridden": "src/theme/PluginThemeComponentOverridden.js",
|
||||
"@theme/PluginThemeComponentEnhanced": "src/theme/PluginThemeComponentEnhanced.js",
|
||||
"@theme/PluginThemeComponentOverriddenByUser": "src/theme/PluginThemeComponentOverriddenByUser.js",
|
||||
"@theme/Root": "../../../../client/theme-fallback/Root/index.js",
|
||||
"@theme/UserThemeComponent1": "src/theme/UserThemeComponent1.js",
|
||||
"@theme/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js",
|
||||
|
|
|
@ -103,6 +103,16 @@ describe('base webpack config', () => {
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
getThemePath() {
|
||||
return path.resolve(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'base_test_site',
|
||||
'secondPluginThemeFolder',
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
@ -182,6 +182,34 @@ Unless you want publish to npm a "theme enhancer" (like `docusaurus-theme-live-c
|
|||
|
||||
:::
|
||||
|
||||
<details>
|
||||
|
||||
<summary>How are theme aliases resolved?</summary>
|
||||
|
||||
It can be quite hard to wrap your mind around these aliases. Let's imagine the following case with a super convoluted setup where three themes/plugins and the site itself all try to define the same component. Internally, Docusaurus loads these themes as a "stack".
|
||||
|
||||
```text
|
||||
+-------------------------------------------------+
|
||||
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
|
||||
+-------------------------------------------------+
|
||||
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
|
||||
+-------------------------------------------------+
|
||||
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
|
||||
+-------------------------------------------------+
|
||||
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
|
||||
+-------------------------------------------------+
|
||||
```
|
||||
|
||||
The components in this "stack" are pushed in the order of `preset plugins > preset themes > plugins > themes > site`, so the swizzled component in `website/src/theme` always comes out on top because it's loaded last.
|
||||
|
||||
`@theme/*` always points to the topmost component—when code block is swizzled, all other components requesting `@theme/CodeBlock` receive the swizzled version.
|
||||
|
||||
`@theme-original/*` always points to the topmost non-swizzled component. That's why you can import `@theme-original/CodeBlock` in the swizzled component—it points to the next one in the "component stack", a theme-provided one. Plugin authors should not try to use this because your component could be the topmost component and cause a self-import.
|
||||
|
||||
`@theme-init/*` always points to the bottommost component—usually this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use `@theme-init/CodeBlock` to get its basic version. Site creators should generally not use this because you likely want to enhance the _topmost_ instead of the _bottommost_ component. It's also possible that the `@theme-init/CodeBlock` alias does not exist at all—Docusaurus only creates it when it points to a different one from `@theme-original/CodeBlock`, i.e. when it's provided by more than one theme. We don't waste aliases!
|
||||
|
||||
</details>
|
||||
|
||||
## Themes design {#themes-design}
|
||||
|
||||
While themes share the exact same lifecycle methods with plugins, their implementations can look very different from those of plugins based on themes' designed objectives.
|
||||
|
|
17
website/src/theme/CodeBlock/index.tsx
Normal file
17
website/src/theme/CodeBlock/index.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type {Props} from '@theme/CodeBlock';
|
||||
import CodeBlock from '@theme-original/CodeBlock';
|
||||
|
||||
// This component does nothing on purpose
|
||||
// Dogfood: wrapping a theme component already enhanced by another theme
|
||||
// See https://github.com/facebook/docusaurus/pull/5983
|
||||
export default function CodeBlockWrapper(props: Props): JSX.Element {
|
||||
return <CodeBlock {...props} />;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue