feat(v2): debug pages + debug layout + ability to debug content (#3229)

* improve debug plugin:
- add multiple debug pages + debug layout
- ability to debug plugin contentLoaded data

* add missing dependency

* fix broken test

* improve content rendering a bit

* create basic DebugJsonView

* fix ReactJson SSR issues
This commit is contained in:
Sébastien Lorber 2020-08-07 11:47:43 +02:00 committed by GitHub
parent be210a1bc4
commit fe281a8ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 511 additions and 110 deletions

View file

@ -6,13 +6,20 @@
*/
import {LoadContext, Plugin} from '@docusaurus/types';
import {normalizeUrl} from '@docusaurus/utils';
import {docuHash, normalizeUrl} from '@docusaurus/utils';
import path from 'path';
export default function pluginContentPages({
siteConfig: {baseUrl},
generatedFilesDir,
}: LoadContext): Plugin<void> {
const pluginDataDirRoot = path.join(
generatedFilesDir,
'docusaurus-plugin-debug',
);
const aliasedSource = (source: string) =>
`~debug/${path.relative(pluginDataDirRoot, source)}`;
return {
name: 'docusaurus-plugin-debug',
@ -20,12 +27,63 @@ export default function pluginContentPages({
return path.resolve(__dirname, '../src/theme');
},
contentLoaded({actions: {addRoute}}) {
async contentLoaded({actions: {createData, addRoute}, allContent}) {
const allContentPath = await createData(
// Note that this created data path must be in sync with
// metadataPath provided to mdx-loader.
`${docuHash('docusaurus-debug-allContent')}.json`,
JSON.stringify(allContent, null, 2),
);
// Home is config (duplicate for now)
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug']),
component: '@theme/Debug',
component: '@theme/DebugConfig',
exact: true,
});
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug/config']),
component: '@theme/DebugConfig',
exact: true,
});
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug/metadata']),
component: '@theme/DebugMetadata',
exact: true,
});
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug/registry']),
component: '@theme/DebugRegistry',
exact: true,
});
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug/routes']),
component: '@theme/DebugRoutes',
exact: true,
});
addRoute({
path: normalizeUrl([baseUrl, '__docusaurus/debug/content']),
component: '@theme/DebugContent',
exact: true,
modules: {
allContent: aliasedSource(allContentPath),
},
});
},
configureWebpack() {
return {
resolve: {
alias: {
'~debug': pluginDataDirRoot,
},
},
};
},
};
}

View file

@ -1,70 +0,0 @@
/**
* 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 Layout from '@theme/Layout';
import registry from '@generated/registry';
import routes from '@generated/routes';
import styles from './styles.module.css';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
function Debug() {
const {siteMetadata} = useDocusaurusContext();
return (
<Layout permalink="__docusaurus/debug" title="Debug">
<main className={styles.Container}>
<section className={styles.Section}>
<h2>Site Metadata</h2>
<div>Docusaurus Version: {siteMetadata.docusaurusVersion}</div>
<div>
Site Version: {siteMetadata.siteVersion || 'No version specified'}
</div>
<h3>Plugins and themes:</h3>
<ul>
{Object.entries(siteMetadata.pluginVersions).map(
([name, versionInformation]) => (
<li key={name}>
<div>Name: {name}</div>
<div>Type: {versionInformation.type}</div>
{versionInformation.version && (
<div>Version: {versionInformation.version}</div>
)}
</li>
),
)}
</ul>
</section>
<section className={styles.Section}>
<h2>Registry</h2>
<ul>
{Object.values(registry).map(([, aliasedPath, resolved]) => (
<li key={aliasedPath}>
<div>Aliased Path: {aliasedPath}</div>
<div>Resolved Path: {resolved}</div>
</li>
))}
</ul>
</section>
<section className={styles.Section}>
<h2>Routes</h2>
<ul>
{routes.map(({path, exact}) => (
<li key={path}>
<div>Route: {path}</div>
<div>Is exact: {String(Boolean(exact))}</div>
</li>
))}
</ul>
</section>
</main>
</Layout>
);
}
export default Debug;

View file

@ -0,0 +1,23 @@
/**
* 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 DebugLayout from '../DebugLayout';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
function DebugMetadata() {
const {siteConfig} = useDocusaurusContext();
return (
<DebugLayout>
<h2>Site config</h2>
<div>{JSON.stringify(siteConfig, null, 2)}</div>
</DebugLayout>
);
}
export default DebugMetadata;

View file

@ -5,13 +5,3 @@
* LICENSE file in the root directory of this source tree.
*/
.Container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin: 1em;
}
.Section {
width: 500px;
}

View file

@ -0,0 +1,84 @@
/**
* 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, {useState} from 'react';
import DebugLayout from '../DebugLayout';
import DebugJsonView from '../DebugJsonView';
const PluginInstanceContent = ({pluginId, pluginInstanceContent}) => (
<section style={{marginBottom: 30}}>
<h4>{`>> ${pluginId}`}</h4>
<div
style={{
marginTop: 10,
padding: 10,
border: 'thin cyan solid',
borderRadius: 5,
backgroundColor: 'lightgrey',
}}>
<DebugJsonView src={pluginInstanceContent} />
</div>
</section>
);
const PluginContent = ({pluginName, pluginContent}) => {
const [visible, setVisible] = useState(true);
return (
<section style={{marginBottom: 60}}>
<h3 onClick={() => setVisible((v) => !v)} style={{cursor: 'pointer'}}>
{pluginName}
</h3>
{visible && (
<div>
{Object.entries(pluginContent)
// filter plugin instances with no content
.filter(
([_pluginId, pluginInstanceContent]) => !!pluginInstanceContent,
)
.map(([pluginId, pluginInstanceContent]) => {
return (
<PluginInstanceContent
key={pluginId}
pluginId={pluginId}
pluginInstanceContent={pluginInstanceContent}
/>
);
})}
</div>
)}
</section>
);
};
function DebugContent({allContent}) {
return (
<DebugLayout>
<h2>Plugin content</h2>
<div>
{Object.entries(allContent)
// filter plugins with no content
.filter(([_pluginName, pluginContent]) =>
Object.values(pluginContent).some(
(instanceContent) => !!instanceContent,
),
)
.map(([pluginName, pluginContent]) => {
return (
<PluginContent
key={pluginName}
pluginName={pluginName}
pluginContent={pluginContent}
/>
);
})}
</div>
</DebugLayout>
);
}
export default DebugContent;

View file

@ -0,0 +1,7 @@
/**
* 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.
*/

View file

@ -0,0 +1,43 @@
/**
* 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 BrowserOnly from '@docusaurus/BrowserOnly';
// avoids "react-json-view" to display "root"
const RootName = false;
// Seems ReactJson does not work with SSR
// https://github.com/mac-s-g/react-json-view/issues/121
const BrowserOnlyReactJson = (props) => {
return (
<BrowserOnly>
{() => {
const ReactJson = require('react-json-view').default;
return <ReactJson {...props} />;
}}
</BrowserOnly>
);
};
function DebugJsonView({src}) {
return (
<BrowserOnlyReactJson
src={src}
name={RootName}
shouldCollapse={(field) => {
// By default, we collapse the json for performance reasons
// See https://github.com/mac-s-g/react-json-view/issues/235
// only the "root" is not collapsed
return field.name !== RootName;
}}
/>
);
}
export default DebugJsonView;

View file

@ -0,0 +1,7 @@
/**
* 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.
*/

View file

@ -0,0 +1,39 @@
/**
* 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 Link from '@docusaurus/Link';
// import styles from './styles.module.css';
const DebugNavLink = ({to, children}) => (
<Link
style={{margin: 10}}
className="button button--primary"
isNavLink
activeClassName="button--active"
to={to}
exact>
{children}
</Link>
);
function DebugLayout({children}) {
return (
<div>
<nav style={{width: '100%', padding: 10, border: 'solid'}}>
<DebugNavLink to="/__docusaurus/debug">Config</DebugNavLink>
<DebugNavLink to="/__docusaurus/debug/metadata">Metadata</DebugNavLink>
<DebugNavLink to="/__docusaurus/debug/registry">Registry</DebugNavLink>
<DebugNavLink to="/__docusaurus/debug/routes">Routes</DebugNavLink>
<DebugNavLink to="/__docusaurus/debug/content">Content</DebugNavLink>
</nav>
<main style={{padding: 20}}>{children}</main>
</div>
);
}
export default DebugLayout;

View file

@ -0,0 +1,6 @@
/**
* 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.
*/

View file

@ -0,0 +1,40 @@
/**
* 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 DebugLayout from '../DebugLayout';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
function DebugMetadata() {
const {siteMetadata} = useDocusaurusContext();
return (
<DebugLayout>
<h2>Site Metadata</h2>
<div>Docusaurus Version: {siteMetadata.docusaurusVersion}</div>
<div>
Site Version: {siteMetadata.siteVersion || 'No version specified'}
</div>
<h3>Plugins and themes:</h3>
<ul>
{Object.entries(siteMetadata.pluginVersions).map(
([name, versionInformation]) => (
<li key={name}>
<div>Name: {name}</div>
<div>Type: {versionInformation.type}</div>
{versionInformation.version && (
<div>Version: {versionInformation.version}</div>
)}
</li>
),
)}
</ul>
</DebugLayout>
);
}
export default DebugMetadata;

View file

@ -0,0 +1,7 @@
/**
* 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.
*/

View file

@ -0,0 +1,30 @@
/**
* 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 DebugLayout from '../DebugLayout';
import registry from '@generated/registry';
function DebugRegistry() {
return (
<DebugLayout>
{' '}
<h2>Registry</h2>
<ul>
{Object.values(registry).map(([, aliasedPath, resolved]) => (
<li key={aliasedPath}>
<div>Aliased Path: {aliasedPath}</div>
<div>Resolved Path: {resolved}</div>
</li>
))}
</ul>
</DebugLayout>
);
}
export default DebugRegistry;

View file

@ -0,0 +1,7 @@
/**
* 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.
*/

View file

@ -0,0 +1,29 @@
/**
* 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 DebugLayout from '../DebugLayout';
import routes from '@generated/routes';
function DebugRoutes() {
return (
<DebugLayout>
<h2>Routes</h2>
<ul>
{routes.map(({path, exact}) => (
<li key={path}>
<div>Route: {path}</div>
<div>Is exact: {String(Boolean(exact))}</div>
</li>
))}
</ul>
</DebugLayout>
);
}
export default DebugRoutes;

View file

@ -0,0 +1,7 @@
/**
* 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.
*/