mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 23:57:22 +02:00
feat(core): fail-safe global data fetching (#7083)
This commit is contained in:
parent
9145ae88cc
commit
171927342f
8 changed files with 51 additions and 26 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 '/'
|
||||
|
|
|
@ -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} => {
|
||||
|
|
|
@ -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;
|
||||
|
|
8
packages/docusaurus-types/src/index.d.ts
vendored
8
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
@ -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.`,
|
||||
);
|
||||
|
@ -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}".`,
|
||||
);
|
||||
|
|
|
@ -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:
|
||||
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue