From fcaa94695d76ad508ff4dcdc1a2cabbe1a22650e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 4 Dec 2021 01:59:45 +0800 Subject: [PATCH] fix(core): do not apply theme-init alias to user component (#5983) Co-authored-by: sebastienlorber --- .../src/server/themes/__tests__/index.test.ts | 2 +- .../docusaurus/src/server/themes/index.ts | 33 +++++++++---------- ...den.js => PluginThemeComponentEnhanced.js} | 0 .../PluginThemeComponentOverriddenByUser.js} | 0 .../PluginThemeComponentEnhanced.js | 0 .../src/theme/PluginThemeComponentEnhanced.js | 0 .../PluginThemeComponentOverriddenByUser.js | 0 .../__tests__/__snapshots__/base.test.ts.snap | 8 +++-- .../src/webpack/__tests__/base.test.ts | 10 ++++++ website/docs/using-themes.md | 28 ++++++++++++++++ website/src/theme/CodeBlock/index.tsx | 17 ++++++++++ 11 files changed, 76 insertions(+), 22 deletions(-) rename packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/{PluginThemeComponentOverridden.js => PluginThemeComponentEnhanced.js} (100%) rename packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/{src/theme/PluginThemeComponentOverridden.js => pluginThemeFolder/PluginThemeComponentOverriddenByUser.js} (100%) create mode 100644 packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/secondPluginThemeFolder/PluginThemeComponentEnhanced.js create mode 100644 packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentEnhanced.js create mode 100644 packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentOverriddenByUser.js create mode 100644 website/src/theme/CodeBlock/index.tsx diff --git a/packages/docusaurus/src/server/themes/__tests__/index.test.ts b/packages/docusaurus/src/server/themes/__tests__/index.test.ts index 7ce9e042f4..157ec169d3 100644 --- a/packages/docusaurus/src/server/themes/__tests__/index.test.ts +++ b/packages/docusaurus/src/server/themes/__tests__/index.test.ts @@ -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( diff --git a/packages/docusaurus/src/server/themes/index.ts b/packages/docusaurus/src/server/themes/index.ts index 37cecdc9ca..cc92463678 100644 --- a/packages/docusaurus/src/server/themes/index.ts +++ b/packages/docusaurus/src/server/themes/index.ts @@ -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); diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentOverridden.js b/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentEnhanced.js similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentOverridden.js rename to packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentEnhanced.js diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentOverridden.js b/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentOverriddenByUser.js similarity index 100% rename from packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentOverridden.js rename to packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/pluginThemeFolder/PluginThemeComponentOverriddenByUser.js diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/secondPluginThemeFolder/PluginThemeComponentEnhanced.js b/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/secondPluginThemeFolder/PluginThemeComponentEnhanced.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentEnhanced.js b/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentEnhanced.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentOverriddenByUser.js b/packages/docusaurus/src/webpack/__tests__/__fixtures__/base_test_site/src/theme/PluginThemeComponentOverriddenByUser.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap index 1a92081d41..623fa86a83 100644 --- a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap +++ b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap @@ -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", diff --git a/packages/docusaurus/src/webpack/__tests__/base.test.ts b/packages/docusaurus/src/webpack/__tests__/base.test.ts index 7d2af48b4b..564395e812 100644 --- a/packages/docusaurus/src/webpack/__tests__/base.test.ts +++ b/packages/docusaurus/src/webpack/__tests__/base.test.ts @@ -103,6 +103,16 @@ describe('base webpack config', () => { ); }, }, + { + getThemePath() { + return path.resolve( + __dirname, + '__fixtures__', + 'base_test_site', + 'secondPluginThemeFolder', + ); + }, + }, ], }; diff --git a/website/docs/using-themes.md b/website/docs/using-themes.md index 85633ea551..77e9f4a71d 100644 --- a/website/docs/using-themes.md +++ b/website/docs/using-themes.md @@ -182,6 +182,34 @@ Unless you want publish to npm a "theme enhancer" (like `docusaurus-theme-live-c ::: +
+ +How are theme aliases resolved? + +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! + +
+ ## 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. diff --git a/website/src/theme/CodeBlock/index.tsx b/website/src/theme/CodeBlock/index.tsx new file mode 100644 index 0000000000..dd5aedfc18 --- /dev/null +++ b/website/src/theme/CodeBlock/index.tsx @@ -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 ; +}