From 171927342fca8dafe77093bf5dbc8e81df9055f2 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 8 Apr 2022 17:08:22 +0800 Subject: [PATCH] feat(core): fail-safe global data fetching (#7083) --- .../src/index.d.ts | 8 ++++--- .../src/client/docsClientUtils.ts | 4 ++-- .../src/client/index.ts | 23 +++++++++++-------- .../src/plugin-content-docs.d.ts | 7 +++--- packages/docusaurus-types/src/index.d.ts | 8 +++++++ .../exports/__tests__/useGlobalData.test.tsx | 4 ++-- .../src/client/exports/useGlobalData.ts | 12 ++++++---- website/docs/docusaurus-core.md | 11 +++++++-- 8 files changed, 51 insertions(+), 26 deletions(-) diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 9d98309ef69c..dc196b86f210 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -315,16 +315,18 @@ declare module '@docusaurus/renderRoutes' { } declare module '@docusaurus/useGlobalData' { - import type {GlobalData} from '@docusaurus/types'; + import type {GlobalData, UseDataOptions} from '@docusaurus/types'; export function useAllPluginInstancesData( pluginName: string, - ): GlobalData[string]; + options?: UseDataOptions, + ): GlobalData[string] | undefined; export function usePluginData( pluginName: string, pluginId?: string, - ): GlobalData[string][string]; + options?: UseDataOptions, + ): GlobalData[string][string] | undefined; export default function useGlobalData(): GlobalData; } diff --git a/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts b/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts index 1a07cd180735..a6e3359fc52f 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts @@ -11,11 +11,11 @@ import type { GlobalPluginData, GlobalVersion, GlobalDoc, - GetActivePluginOptions, ActivePlugin, ActiveDocContext, DocVersionSuggestions, } from '@docusaurus/plugin-content-docs/client'; +import type {UseDataOptions} from '@docusaurus/types'; // This code is not part of the api surface, not in ./theme on purpose @@ -25,7 +25,7 @@ import type { export function getActivePlugin( allPluginData: {[pluginId: string]: GlobalPluginData}, pathname: string, - options: GetActivePluginOptions = {}, + options: UseDataOptions = {}, ): ActivePlugin | undefined { const activeEntry = Object.entries(allPluginData) // Route sorting: '/android/foo' should match '/android' instead of '/' diff --git a/packages/docusaurus-plugin-content-docs/src/client/index.ts b/packages/docusaurus-plugin-content-docs/src/client/index.ts index 803456fbbe7d..4923a069d0a6 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/index.ts @@ -6,7 +6,10 @@ */ import {useLocation} from '@docusaurus/router'; -import useGlobalData, {usePluginData} from '@docusaurus/useGlobalData'; +import { + useAllPluginInstancesData, + usePluginData, +} from '@docusaurus/useGlobalData'; import { getActivePlugin, @@ -21,25 +24,27 @@ import type { ActivePlugin, ActiveDocContext, DocVersionSuggestions, - GetActivePluginOptions, } from '@docusaurus/plugin-content-docs/client'; +import type {UseDataOptions} from '@docusaurus/types'; // Important to use a constant object to avoid React useEffect executions etc. // see https://github.com/facebook/docusaurus/issues/5089 const StableEmptyObject = {}; -// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks -// are still used by the theme. We need a fail-safe fallback when the docs -// plugin is not in use +// In blog-only mode, docs hooks are still used by the theme. We need a fail- +// safe fallback when the docs plugin is not in use export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} => - useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject; + useAllPluginInstancesData('docusaurus-plugin-content-docs') ?? + StableEmptyObject; export const useDocsData = (pluginId: string | undefined): GlobalPluginData => - usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData; + usePluginData('docusaurus-plugin-content-docs', pluginId, { + failfast: true, + }) as GlobalPluginData; // TODO this feature should be provided by docusaurus core export const useActivePlugin = ( - options: GetActivePluginOptions = {}, + options: UseDataOptions = {}, ): ActivePlugin | undefined => { const data = useAllDocsData(); const {pathname} = useLocation(); @@ -47,7 +52,7 @@ export const useActivePlugin = ( }; export const useActivePluginAndVersion = ( - options: GetActivePluginOptions = {}, + options: UseDataOptions = {}, ): | undefined | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => { diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 77d4b7b894c0..7bc5a2419430 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -612,6 +612,8 @@ declare module '@theme/DocPage/Layout/Main' { // TODO until TS supports exports field... hope it's in 4.6 declare module '@docusaurus/plugin-content-docs/client' { + import type {UseDataOptions} from '@docusaurus/types'; + export type ActivePlugin = { pluginId: string; pluginData: GlobalPluginData; @@ -655,15 +657,14 @@ declare module '@docusaurus/plugin-content-docs/client' { // suggest the same doc, in latest version (if exist) latestDocSuggestion?: GlobalDoc; }; - export type GetActivePluginOptions = {failfast?: boolean}; // use fail-fast option if you know for sure one plugin instance is active export const useAllDocsData: () => {[pluginId: string]: GlobalPluginData}; export const useDocsData: (pluginId?: string) => GlobalPluginData; export const useActivePlugin: ( - options?: GetActivePluginOptions, + options?: UseDataOptions, ) => ActivePlugin | undefined; export const useActivePluginAndVersion: ( - options?: GetActivePluginOptions, + options?: UseDataOptions, ) => | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} | undefined; diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index e20bd74ee6a5..9c19ac7edcc6 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -619,3 +619,11 @@ export type TagModule = TagsListItem & { /** The tags list page's permalink. */ allTagsPath: string; }; + +export type UseDataOptions = { + /** + * Throw an error, or simply return undefined if the data cannot be found. Use + * `true` if you are sure the data must exist. + */ + failfast?: boolean; +}; diff --git a/packages/docusaurus/src/client/exports/__tests__/useGlobalData.test.tsx b/packages/docusaurus/src/client/exports/__tests__/useGlobalData.test.tsx index c2ec645c2887..b688427aa9a5 100644 --- a/packages/docusaurus/src/client/exports/__tests__/useGlobalData.test.tsx +++ b/packages/docusaurus/src/client/exports/__tests__/useGlobalData.test.tsx @@ -59,7 +59,7 @@ describe('useAllPluginInstancesData', () => { it('throws when plugin data not found', () => { expect( () => - renderHook(() => useAllPluginInstancesData('bar'), { + renderHook(() => useAllPluginInstancesData('bar', {failfast: true}), { wrapper: ({children}) => ( { it('throws when plugin instance data not found', () => { expect( () => - renderHook(() => usePluginData('foo', 'baz'), { + renderHook(() => usePluginData('foo', 'baz', {failfast: true}), { wrapper: ({children}) => ( { Access global data created by a specific plugin. Given a plugin name, it returns the data of all the plugins instances of that name, by plugin id. ```ts -useAllPluginInstancesData(pluginName: string) +function useAllPluginInstancesData( + pluginName: string, + options?: {failfast?: boolean}, +); ``` Usage example: