mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 08:07:26 +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' {
|
declare module '@docusaurus/useGlobalData' {
|
||||||
import type {GlobalData} from '@docusaurus/types';
|
import type {GlobalData, UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
export function useAllPluginInstancesData(
|
export function useAllPluginInstancesData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
): GlobalData[string];
|
options?: UseDataOptions,
|
||||||
|
): GlobalData[string] | undefined;
|
||||||
|
|
||||||
export function usePluginData(
|
export function usePluginData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
pluginId?: string,
|
pluginId?: string,
|
||||||
): GlobalData[string][string];
|
options?: UseDataOptions,
|
||||||
|
): GlobalData[string][string] | undefined;
|
||||||
|
|
||||||
export default function useGlobalData(): GlobalData;
|
export default function useGlobalData(): GlobalData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ import type {
|
||||||
GlobalPluginData,
|
GlobalPluginData,
|
||||||
GlobalVersion,
|
GlobalVersion,
|
||||||
GlobalDoc,
|
GlobalDoc,
|
||||||
GetActivePluginOptions,
|
|
||||||
ActivePlugin,
|
ActivePlugin,
|
||||||
ActiveDocContext,
|
ActiveDocContext,
|
||||||
DocVersionSuggestions,
|
DocVersionSuggestions,
|
||||||
} from '@docusaurus/plugin-content-docs/client';
|
} 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
|
// This code is not part of the api surface, not in ./theme on purpose
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import type {
|
||||||
export function getActivePlugin(
|
export function getActivePlugin(
|
||||||
allPluginData: {[pluginId: string]: GlobalPluginData},
|
allPluginData: {[pluginId: string]: GlobalPluginData},
|
||||||
pathname: string,
|
pathname: string,
|
||||||
options: GetActivePluginOptions = {},
|
options: UseDataOptions = {},
|
||||||
): ActivePlugin | undefined {
|
): ActivePlugin | undefined {
|
||||||
const activeEntry = Object.entries(allPluginData)
|
const activeEntry = Object.entries(allPluginData)
|
||||||
// Route sorting: '/android/foo' should match '/android' instead of '/'
|
// Route sorting: '/android/foo' should match '/android' instead of '/'
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useLocation} from '@docusaurus/router';
|
import {useLocation} from '@docusaurus/router';
|
||||||
import useGlobalData, {usePluginData} from '@docusaurus/useGlobalData';
|
import {
|
||||||
|
useAllPluginInstancesData,
|
||||||
|
usePluginData,
|
||||||
|
} from '@docusaurus/useGlobalData';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getActivePlugin,
|
getActivePlugin,
|
||||||
|
@ -21,25 +24,27 @@ import type {
|
||||||
ActivePlugin,
|
ActivePlugin,
|
||||||
ActiveDocContext,
|
ActiveDocContext,
|
||||||
DocVersionSuggestions,
|
DocVersionSuggestions,
|
||||||
GetActivePluginOptions,
|
|
||||||
} from '@docusaurus/plugin-content-docs/client';
|
} from '@docusaurus/plugin-content-docs/client';
|
||||||
|
import type {UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
// Important to use a constant object to avoid React useEffect executions etc.
|
// Important to use a constant object to avoid React useEffect executions etc.
|
||||||
// see https://github.com/facebook/docusaurus/issues/5089
|
// see https://github.com/facebook/docusaurus/issues/5089
|
||||||
const StableEmptyObject = {};
|
const StableEmptyObject = {};
|
||||||
|
|
||||||
// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks
|
// In blog-only mode, docs hooks are still used by the theme. We need a fail-
|
||||||
// are still used by the theme. We need a fail-safe fallback when the docs
|
// safe fallback when the docs plugin is not in use
|
||||||
// plugin is not in use
|
|
||||||
export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} =>
|
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 =>
|
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
|
// TODO this feature should be provided by docusaurus core
|
||||||
export const useActivePlugin = (
|
export const useActivePlugin = (
|
||||||
options: GetActivePluginOptions = {},
|
options: UseDataOptions = {},
|
||||||
): ActivePlugin | undefined => {
|
): ActivePlugin | undefined => {
|
||||||
const data = useAllDocsData();
|
const data = useAllDocsData();
|
||||||
const {pathname} = useLocation();
|
const {pathname} = useLocation();
|
||||||
|
@ -47,7 +52,7 @@ export const useActivePlugin = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useActivePluginAndVersion = (
|
export const useActivePluginAndVersion = (
|
||||||
options: GetActivePluginOptions = {},
|
options: UseDataOptions = {},
|
||||||
):
|
):
|
||||||
| undefined
|
| undefined
|
||||||
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | 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
|
// TODO until TS supports exports field... hope it's in 4.6
|
||||||
declare module '@docusaurus/plugin-content-docs/client' {
|
declare module '@docusaurus/plugin-content-docs/client' {
|
||||||
|
import type {UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
export type ActivePlugin = {
|
export type ActivePlugin = {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
pluginData: GlobalPluginData;
|
pluginData: GlobalPluginData;
|
||||||
|
@ -655,15 +657,14 @@ declare module '@docusaurus/plugin-content-docs/client' {
|
||||||
// suggest the same doc, in latest version (if exist)
|
// suggest the same doc, in latest version (if exist)
|
||||||
latestDocSuggestion?: GlobalDoc;
|
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 useAllDocsData: () => {[pluginId: string]: GlobalPluginData};
|
||||||
export const useDocsData: (pluginId?: string) => GlobalPluginData;
|
export const useDocsData: (pluginId?: string) => GlobalPluginData;
|
||||||
export const useActivePlugin: (
|
export const useActivePlugin: (
|
||||||
options?: GetActivePluginOptions,
|
options?: UseDataOptions,
|
||||||
) => ActivePlugin | undefined;
|
) => ActivePlugin | undefined;
|
||||||
export const useActivePluginAndVersion: (
|
export const useActivePluginAndVersion: (
|
||||||
options?: GetActivePluginOptions,
|
options?: UseDataOptions,
|
||||||
) =>
|
) =>
|
||||||
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
|
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
|
||||||
| 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. */
|
/** The tags list page's permalink. */
|
||||||
allTagsPath: string;
|
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', () => {
|
it('throws when plugin data not found', () => {
|
||||||
expect(
|
expect(
|
||||||
() =>
|
() =>
|
||||||
renderHook(() => useAllPluginInstancesData('bar'), {
|
renderHook(() => useAllPluginInstancesData('bar', {failfast: true}), {
|
||||||
wrapper: ({children}) => (
|
wrapper: ({children}) => (
|
||||||
<Context.Provider
|
<Context.Provider
|
||||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||||
|
@ -106,7 +106,7 @@ describe('usePluginData', () => {
|
||||||
it('throws when plugin instance data not found', () => {
|
it('throws when plugin instance data not found', () => {
|
||||||
expect(
|
expect(
|
||||||
() =>
|
() =>
|
||||||
renderHook(() => usePluginData('foo', 'baz'), {
|
renderHook(() => usePluginData('foo', 'baz', {failfast: true}), {
|
||||||
wrapper: ({children}) => (
|
wrapper: ({children}) => (
|
||||||
<Context.Provider
|
<Context.Provider
|
||||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import useDocusaurusContext from './useDocusaurusContext';
|
import useDocusaurusContext from './useDocusaurusContext';
|
||||||
import {DEFAULT_PLUGIN_ID} from './constants';
|
import {DEFAULT_PLUGIN_ID} from './constants';
|
||||||
import type {GlobalData} from '@docusaurus/types';
|
import type {GlobalData, UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
export default function useGlobalData(): GlobalData {
|
export default function useGlobalData(): GlobalData {
|
||||||
const {globalData} = useDocusaurusContext();
|
const {globalData} = useDocusaurusContext();
|
||||||
|
@ -19,10 +19,11 @@ export default function useGlobalData(): GlobalData {
|
||||||
|
|
||||||
export function useAllPluginInstancesData(
|
export function useAllPluginInstancesData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
): GlobalData[string] {
|
options: UseDataOptions = {},
|
||||||
|
): GlobalData[string] | undefined {
|
||||||
const globalData = useGlobalData();
|
const globalData = useGlobalData();
|
||||||
const pluginGlobalData = globalData[pluginName];
|
const pluginGlobalData = globalData[pluginName];
|
||||||
if (!pluginGlobalData) {
|
if (!pluginGlobalData && options.failfast) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Docusaurus plugin global data not found for "${pluginName}" plugin.`,
|
`Docusaurus plugin global data not found for "${pluginName}" plugin.`,
|
||||||
);
|
);
|
||||||
|
@ -33,10 +34,11 @@ export function useAllPluginInstancesData(
|
||||||
export function usePluginData(
|
export function usePluginData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
pluginId: string = DEFAULT_PLUGIN_ID,
|
pluginId: string = DEFAULT_PLUGIN_ID,
|
||||||
|
options: UseDataOptions = {},
|
||||||
): GlobalData[string][string] {
|
): GlobalData[string][string] {
|
||||||
const pluginGlobalData = useAllPluginInstancesData(pluginName);
|
const pluginGlobalData = useAllPluginInstancesData(pluginName);
|
||||||
const pluginInstanceGlobalData = pluginGlobalData[pluginId];
|
const pluginInstanceGlobalData = pluginGlobalData?.[pluginId];
|
||||||
if (!pluginInstanceGlobalData) {
|
if (!pluginInstanceGlobalData && options.failfast) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Docusaurus plugin global data not found for "${pluginName}" plugin with id "${pluginId}".`,
|
`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.
|
`pluginId` is optional if you don't use multi-instance plugins.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
function usePluginData(pluginName: string, pluginId?: string);
|
function usePluginData(
|
||||||
|
pluginName: string,
|
||||||
|
pluginId?: string,
|
||||||
|
options?: {failfast?: boolean},
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage example:
|
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.
|
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
|
```ts
|
||||||
useAllPluginInstancesData(pluginName: string)
|
function useAllPluginInstancesData(
|
||||||
|
pluginName: string,
|
||||||
|
options?: {failfast?: boolean},
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage example:
|
Usage example:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue