Skip to content

Commit

Permalink
feat(core): fail-safe global data fetching (#7083)
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh-Cena authored Apr 8, 2022
1 parent 9145ae8 commit 1719273
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 26 deletions.
8 changes: 5 additions & 3 deletions packages/docusaurus-module-type-aliases/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 '/'
Expand Down
23 changes: 14 additions & 9 deletions packages/docusaurus-plugin-content-docs/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
*/

import {useLocation} from '@docusaurus/router';
import useGlobalData, {usePluginData} from '@docusaurus/useGlobalData';
import {
useAllPluginInstancesData,
usePluginData,
} from '@docusaurus/useGlobalData';

import {
getActivePlugin,
Expand All @@ -21,33 +24,35 @@ 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();
return getActivePlugin(data, pathname, options);
};

export const useActivePluginAndVersion = (
options: GetActivePluginOptions = {},
options: UseDataOptions = {},
):
| undefined
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('useAllPluginInstancesData', () => {
it('throws when plugin data not found', () => {
expect(
() =>
renderHook(() => useAllPluginInstancesData('bar'), {
renderHook(() => useAllPluginInstancesData('bar', {failfast: true}), {
wrapper: ({children}) => (
<Context.Provider
// eslint-disable-next-line react/jsx-no-constructed-context-values
Expand Down Expand Up @@ -106,7 +106,7 @@ describe('usePluginData', () => {
it('throws when plugin instance data not found', () => {
expect(
() =>
renderHook(() => usePluginData('foo', 'baz'), {
renderHook(() => usePluginData('foo', 'baz', {failfast: true}), {
wrapper: ({children}) => (
<Context.Provider
// eslint-disable-next-line react/jsx-no-constructed-context-values
Expand Down
12 changes: 7 additions & 5 deletions packages/docusaurus/src/client/exports/useGlobalData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import useDocusaurusContext from './useDocusaurusContext';
import {DEFAULT_PLUGIN_ID} from './constants';
import type {GlobalData} from '@docusaurus/types';
import type {GlobalData, UseDataOptions} from '@docusaurus/types';

export default function useGlobalData(): GlobalData {
const {globalData} = useDocusaurusContext();
Expand All @@ -19,10 +19,11 @@ export default function useGlobalData(): GlobalData {

export function useAllPluginInstancesData(
pluginName: string,
): GlobalData[string] {
options: UseDataOptions = {},
): GlobalData[string] | undefined {
const globalData = useGlobalData();
const pluginGlobalData = globalData[pluginName];
if (!pluginGlobalData) {
if (!pluginGlobalData && options.failfast) {
throw new Error(
`Docusaurus plugin global data not found for "${pluginName}" plugin.`,
);
Expand All @@ -33,10 +34,11 @@ export function useAllPluginInstancesData(
export function usePluginData(
pluginName: string,
pluginId: string = DEFAULT_PLUGIN_ID,
options: UseDataOptions = {},
): GlobalData[string][string] {
const pluginGlobalData = useAllPluginInstancesData(pluginName);
const pluginInstanceGlobalData = pluginGlobalData[pluginId];
if (!pluginInstanceGlobalData) {
const pluginInstanceGlobalData = pluginGlobalData?.[pluginId];
if (!pluginInstanceGlobalData && options.failfast) {
throw new Error(
`Docusaurus plugin global data not found for "${pluginName}" plugin with id "${pluginId}".`,
);
Expand Down
11 changes: 9 additions & 2 deletions website/docs/docusaurus-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,11 @@ This is the most convenient hook to access plugin global data and should be used
`pluginId` is optional if you don't use multi-instance plugins.

```ts
function usePluginData(pluginName: string, pluginId?: string);
function usePluginData(
pluginName: string,
pluginId?: string,
options?: {failfast?: boolean},
);
```

Usage example:
Expand All @@ -567,7 +571,10 @@ const MyComponent = () => {
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:
Expand Down

0 comments on commit 1719273

Please sign in to comment.