mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-12 00:27:21 +02:00
feat(v2): implement theme component overriding (#1435)
* feat(v2): implement component overriding * siteDir theme overriding should work for > 1 level directory * fallback for essential component like Loading * rename default -> classic
This commit is contained in:
parent
1697f9cebb
commit
eedd4c481c
38 changed files with 529 additions and 202 deletions
|
@ -16,6 +16,11 @@
|
||||||
"loader-utils": "^1.2.3"
|
"loader-utils": "^1.2.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@docusaurus/core": "^2.0.0"
|
"@docusaurus/core": "^2.0.0",
|
||||||
|
"react": "^16.8.4",
|
||||||
|
"react-dom": "^16.8.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,22 @@ class DocusaurusPluginContentBlog {
|
||||||
|
|
||||||
configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) {
|
configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) {
|
||||||
return {
|
return {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@theme/BlogListPage': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'./theme/BlogListPage',
|
||||||
|
),
|
||||||
|
'@theme/BlogPostItem': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'./theme/BlogPostItem',
|
||||||
|
),
|
||||||
|
'@theme/BlogPostPage': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'./theme/BlogPostPage',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
import BlogPostItem from '../BlogPostItem';
|
import BlogPostItem from '@theme/BlogPostItem';
|
||||||
|
|
||||||
function BlogListPage(props) {
|
function BlogListPage(props) {
|
||||||
const {
|
const {
|
|
@ -8,7 +8,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
import BlogPostItem from '../BlogPostItem';
|
import BlogPostItem from '@theme/BlogPostItem';
|
||||||
|
|
||||||
function BlogPostPage(props) {
|
function BlogPostPage(props) {
|
||||||
const {content: BlogPostContents, metadata} = props;
|
const {content: BlogPostContents, metadata} = props;
|
|
@ -18,6 +18,12 @@
|
||||||
"loader-utils": "^1.2.3"
|
"loader-utils": "^1.2.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@docusaurus/core": "^2.0.0"
|
"@docusaurus/core": "^2.0.0",
|
||||||
|
"react": "^16.8.4",
|
||||||
|
"react-dom": "^16.8.4",
|
||||||
|
"react-router-config": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,17 @@ class DocusaurusPluginContentDocs {
|
||||||
|
|
||||||
configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) {
|
configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) {
|
||||||
return {
|
return {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@theme/DocItem': path.resolve(__dirname, './theme/DocItem'),
|
||||||
|
'@theme/DocPage': path.resolve(__dirname, './theme/DocPage'),
|
||||||
|
'@theme/DocPaginator': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'./theme/DocPaginator',
|
||||||
|
),
|
||||||
|
'@theme/DocSidebar': path.resolve(__dirname, './theme/DocSidebar'),
|
||||||
|
},
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||||
|
|
||||||
import Head from '@docusaurus/Head';
|
import Head from '@docusaurus/Head';
|
||||||
|
|
||||||
import DocPaginator from '../DocPaginator';
|
import DocPaginator from '@theme/DocPaginator';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {renderRoutes} from 'react-router-config';
|
||||||
|
|
||||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
|
|
||||||
import DocSidebar from '../DocSidebar';
|
import DocSidebar from '@theme/DocSidebar';
|
||||||
|
|
||||||
function DocPage(props) {
|
function DocPage(props) {
|
||||||
const {route, docsMetadata, location} = props;
|
const {route, docsMetadata, location} = props;
|
|
@ -12,6 +12,11 @@
|
||||||
"globby": "^9.1.0"
|
"globby": "^9.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@docusaurus/core": "^2.0.0"
|
"@docusaurus/core": "^2.0.0",
|
||||||
|
"react": "^16.8.4",
|
||||||
|
"react-dom": "^16.8.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
module.exports = function preset(context, opts = {}) {
|
module.exports = function preset(context, opts = {}) {
|
||||||
return {
|
return {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/theme-classic',
|
||||||
|
},
|
||||||
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-docs',
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
|
21
packages/docusaurus-theme-classic/package.json
Normal file
21
packages/docusaurus-theme-classic/package.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/theme-classic",
|
||||||
|
"version": "2.0.0-alpha.13",
|
||||||
|
"description": "Classic theme for Docusaurus",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"docsearch.js": "^2.5.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@docusaurus/core": "^2.0.0",
|
||||||
|
"react": "^16.8.4",
|
||||||
|
"react-dom": "^16.8.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
}
|
36
packages/docusaurus-theme-classic/src/index.js
Normal file
36
packages/docusaurus-theme-classic/src/index.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {};
|
||||||
|
|
||||||
|
class DocusaurusThemeDefault {
|
||||||
|
constructor(context, opts) {
|
||||||
|
this.options = {...DEFAULT_OPTIONS, ...opts};
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return 'docusaurus-theme-classic';
|
||||||
|
}
|
||||||
|
|
||||||
|
configureWebpack() {
|
||||||
|
return {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@theme/Footer': path.resolve(__dirname, './theme/Footer'),
|
||||||
|
'@theme/Navbar': path.resolve(__dirname, './theme/Navbar'),
|
||||||
|
'@theme/NotFound': path.resolve(__dirname, './theme/NotFound'),
|
||||||
|
'@theme/Search': path.resolve(__dirname, './theme/Search'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DocusaurusThemeDefault;
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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 Head from '@docusaurus/Head'; // eslint-disable-line
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; // eslint-disable-line
|
||||||
|
|
||||||
|
function Layout(props) {
|
||||||
|
const context = useDocusaurusContext();
|
||||||
|
const {siteConfig = {}} = context;
|
||||||
|
const {baseUrl, favicon, tagline, title: defaultTitle} = siteConfig;
|
||||||
|
const {children, title} = props;
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Head defaultTitle={`${defaultTitle} · ${tagline}`}>
|
||||||
|
{title && <title>{`${title} · ${tagline}`}</title>}
|
||||||
|
{favicon && <link rel="shortcut icon" href={baseUrl + favicon} />}
|
||||||
|
</Head>
|
||||||
|
{children}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout;
|
110
packages/docusaurus/lib/client/theme-fallback/Loading/index.js
Normal file
110
packages/docusaurus/lib/client/theme-fallback/Loading/index.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
export default props => {
|
||||||
|
if (props.error) {
|
||||||
|
console.warn(props.error);
|
||||||
|
return <div align="center">Error</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.pastDelay) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100vh',
|
||||||
|
}}>
|
||||||
|
<svg
|
||||||
|
id="loader"
|
||||||
|
style={{
|
||||||
|
width: 128,
|
||||||
|
height: 110,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 'calc(100vh - 64%)',
|
||||||
|
}}
|
||||||
|
viewBox="0 0 45 45"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
stroke="#61dafb">
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
fillRule="evenodd"
|
||||||
|
transform="translate(1 1)"
|
||||||
|
strokeWidth="2">
|
||||||
|
<circle cx="22" cy="22" r="6" strokeOpacity="0">
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
begin="1.5s"
|
||||||
|
dur="3s"
|
||||||
|
values="6;22"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-opacity"
|
||||||
|
begin="1.5s"
|
||||||
|
dur="3s"
|
||||||
|
values="1;0"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-width"
|
||||||
|
begin="1.5s"
|
||||||
|
dur="3s"
|
||||||
|
values="2;0"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="22" cy="22" r="6" strokeOpacity="0">
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
begin="3s"
|
||||||
|
dur="3s"
|
||||||
|
values="6;22"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-opacity"
|
||||||
|
begin="3s"
|
||||||
|
dur="3s"
|
||||||
|
values="1;0"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-width"
|
||||||
|
begin="3s"
|
||||||
|
dur="3s"
|
||||||
|
values="2;0"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="22" cy="22" r="8">
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
begin="0s"
|
||||||
|
dur="1.5s"
|
||||||
|
values="6;1;2;3;4;5;6"
|
||||||
|
calcMode="linear"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
function NotFound() {
|
||||||
|
return (
|
||||||
|
<Layout title="Page Not Found">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '50vh',
|
||||||
|
fontSize: '20px',
|
||||||
|
}}>
|
||||||
|
<h1>Oops, page not found </h1>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFound;
|
|
@ -84,7 +84,7 @@ module.exports = async function build(siteDir, cliOptions = {}) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run webpack to build js bundle (client) and static html files (server) !!
|
// Run webpack to build JS bundle (client) and static html files (server).
|
||||||
await compile([clientConfig, serverConfig]);
|
await compile([clientConfig, serverConfig]);
|
||||||
|
|
||||||
// Remove server.bundle.js because it is useless
|
// Remove server.bundle.js because it is useless
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2017-present, Facebook, Inc.
|
|
||||||
*
|
|
||||||
* 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 styles from './styles.module.css';
|
|
||||||
|
|
||||||
export default props => {
|
|
||||||
if (props.error) {
|
|
||||||
console.warn(props.error);
|
|
||||||
return <div align="center">Error</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.pastDelay) {
|
|
||||||
return (
|
|
||||||
<div className={styles.loader}>
|
|
||||||
<p>Please wait a moment</p>
|
|
||||||
<div className={styles.loaderSpinning} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2017-present, Facebook, Inc.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderSpinning {
|
|
||||||
width: 49px;
|
|
||||||
height: 49px;
|
|
||||||
margin: 0 auto;
|
|
||||||
border: 5px solid #ccc;
|
|
||||||
border-radius: 50%;
|
|
||||||
border-top: 5px solid #1d4d8b;
|
|
||||||
animation: loader-spin infinite 1s linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loader-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,10 +31,18 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
const context = {siteDir, generatedFilesDir, siteConfig, cliOptions};
|
const context = {siteDir, generatedFilesDir, siteConfig, cliOptions};
|
||||||
|
|
||||||
// Process presets.
|
// Process presets.
|
||||||
const presetPlugins = loadPresets(context);
|
const {plugins: presetPlugins, themes: presetThemes} = loadPresets(context);
|
||||||
|
|
||||||
|
// Process plugins and themes. Themes are also plugins, but they run after all
|
||||||
|
// the explicit plugins because they may override the resolve.alias(es)
|
||||||
|
// defined by the plugins.
|
||||||
|
const pluginConfigs = [
|
||||||
|
...presetPlugins,
|
||||||
|
...(siteConfig.plugins || []),
|
||||||
|
...presetThemes,
|
||||||
|
...(siteConfig.themes || []),
|
||||||
|
];
|
||||||
|
|
||||||
// Process plugins.
|
|
||||||
const pluginConfigs = [...presetPlugins, ...siteConfig.plugins];
|
|
||||||
const {plugins, pluginsRouteConfigs} = await loadPlugins({
|
const {plugins, pluginsRouteConfigs} = await loadPlugins({
|
||||||
pluginConfigs,
|
pluginConfigs,
|
||||||
context,
|
context,
|
||||||
|
@ -43,8 +51,18 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
const outDir = path.resolve(siteDir, 'build');
|
const outDir = path.resolve(siteDir, 'build');
|
||||||
const {baseUrl} = siteConfig;
|
const {baseUrl} = siteConfig;
|
||||||
|
|
||||||
// Resolve theme. TBD (Experimental)
|
// Resolve custom theme override aliases.
|
||||||
const themePath = loadTheme(siteDir);
|
const themeAliases = await loadTheme(siteDir);
|
||||||
|
// Make a fake plugin to resolve user's theme overrides.
|
||||||
|
if (themeAliases != null) {
|
||||||
|
plugins.push({
|
||||||
|
configureWebpack: () => ({
|
||||||
|
resolve: {
|
||||||
|
alias: themeAliases,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
const {
|
const {
|
||||||
|
@ -81,7 +99,6 @@ ${Object.keys(registry)
|
||||||
siteConfig,
|
siteConfig,
|
||||||
siteDir,
|
siteDir,
|
||||||
outDir,
|
outDir,
|
||||||
themePath,
|
|
||||||
baseUrl,
|
baseUrl,
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
routesPaths,
|
routesPaths,
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
module.exports = function preset(context, opts = {}) {
|
||||||
|
return {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/theme-classic',
|
||||||
|
options: opts.test,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-test',
|
||||||
|
options: opts.test,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
|
@ -13,7 +13,12 @@ import loadPresets from '../presets';
|
||||||
describe('loadPresets', () => {
|
describe('loadPresets', () => {
|
||||||
test('no presets', () => {
|
test('no presets', () => {
|
||||||
const presets = loadPresets({siteConfig: {presets: []}});
|
const presets = loadPresets({siteConfig: {presets: []}});
|
||||||
expect(presets).toEqual([]);
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"plugins": Array [],
|
||||||
|
"themes": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('string form', () => {
|
test('string form', () => {
|
||||||
|
@ -23,16 +28,19 @@ describe('loadPresets', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
},
|
"options": undefined,
|
||||||
Object {
|
},
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
},
|
"options": undefined,
|
||||||
]
|
},
|
||||||
|
],
|
||||||
|
"themes": Array [],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,24 +54,27 @@ Array [
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
},
|
"options": undefined,
|
||||||
Object {
|
},
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
},
|
"options": undefined,
|
||||||
Object {
|
},
|
||||||
"name": "@docusaurus/plugin-content-pages",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
},
|
"options": undefined,
|
||||||
Object {
|
},
|
||||||
"name": "@docusaurus/plugin-sitemap",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-sitemap",
|
||||||
},
|
"options": undefined,
|
||||||
]
|
},
|
||||||
|
],
|
||||||
|
"themes": Array [],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,16 +85,19 @@ Array [
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
},
|
"options": undefined,
|
||||||
Object {
|
},
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
Object {
|
||||||
"options": undefined,
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
},
|
"options": undefined,
|
||||||
]
|
},
|
||||||
|
],
|
||||||
|
"themes": Array [],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,18 +113,21 @@ Array [
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": Object {
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
"path": "../",
|
"options": Object {
|
||||||
|
"path": "../",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
"options": undefined,
|
||||||
"options": undefined,
|
},
|
||||||
},
|
],
|
||||||
]
|
"themes": Array [],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -130,28 +147,31 @@ Array [
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": Object {
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
"path": "../",
|
"options": Object {
|
||||||
|
"path": "../",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
"options": undefined,
|
||||||
"options": undefined,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"name": "@docusaurus/plugin-content-pages",
|
|
||||||
"options": Object {
|
|
||||||
"path": "../",
|
|
||||||
},
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
"name": "@docusaurus/plugin-sitemap",
|
"options": Object {
|
||||||
"options": undefined,
|
"path": "../",
|
||||||
},
|
},
|
||||||
]
|
},
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-sitemap",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"themes": Array [],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -168,26 +188,78 @@ Array [
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(presets).toMatchInlineSnapshot(`
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"plugins": Array [
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
Object {
|
||||||
"options": Object {
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
"path": "../",
|
"options": Object {
|
||||||
|
"path": "../",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
"options": undefined,
|
||||||
"options": undefined,
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
"name": "@docusaurus/plugin-content-pages",
|
"options": undefined,
|
||||||
"options": undefined,
|
},
|
||||||
},
|
Object {
|
||||||
Object {
|
"name": "@docusaurus/plugin-sitemap",
|
||||||
"name": "@docusaurus/plugin-sitemap",
|
"options": undefined,
|
||||||
"options": undefined,
|
},
|
||||||
},
|
],
|
||||||
]
|
"themes": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mixed form with themes', () => {
|
||||||
|
const presets = loadPresets({
|
||||||
|
siteConfig: {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
path.join(__dirname, '__fixtures__/preset-bar.js'),
|
||||||
|
{docs: {path: '../'}},
|
||||||
|
],
|
||||||
|
path.join(__dirname, '__fixtures__/preset-foo.js'),
|
||||||
|
path.join(__dirname, '__fixtures__/preset-qux.js'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(presets).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"plugins": Array [
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
|
"options": Object {
|
||||||
|
"path": "../",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-sitemap",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-test",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"themes": Array [
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/theme-classic",
|
||||||
|
"options": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,29 +7,28 @@
|
||||||
|
|
||||||
const importFresh = require('import-fresh');
|
const importFresh = require('import-fresh');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
module.exports = function loadPresets(context) {
|
module.exports = function loadPresets(context) {
|
||||||
const presets = context.siteConfig.presets || [];
|
const presets = context.siteConfig.presets || [];
|
||||||
return _.flatten(
|
const plugins = [];
|
||||||
presets.map(presetItem => {
|
const themes = [];
|
||||||
let presetModule;
|
|
||||||
let presetOptions = {};
|
|
||||||
if (typeof presetItem === 'string') {
|
|
||||||
presetModule = presetItem;
|
|
||||||
} else if (Array.isArray(presetItem)) {
|
|
||||||
[presetModule, presetOptions] = presetItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
let preset;
|
presets.forEach(presetItem => {
|
||||||
if (presetModule && fs.existsSync(presetModule)) {
|
let presetModule;
|
||||||
// Local preset.
|
let presetOptions = {};
|
||||||
preset = importFresh(presetModule);
|
if (typeof presetItem === 'string') {
|
||||||
} else {
|
presetModule = presetItem;
|
||||||
// From npm.
|
} else if (Array.isArray(presetItem)) {
|
||||||
preset = importFresh(presetModule);
|
[presetModule, presetOptions] = presetItem;
|
||||||
}
|
}
|
||||||
return preset(context, presetOptions).plugins;
|
|
||||||
}),
|
const preset = importFresh(presetModule);
|
||||||
);
|
plugins.push(preset(context, presetOptions).plugins);
|
||||||
|
themes.push(preset(context, presetOptions).themes);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: _.compact(_.flatten(plugins)),
|
||||||
|
themes: _.compact(_.flatten(themes)),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,25 +5,32 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const globby = require('globby');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const {fileToPath, posixPath, normalizeUrl} = require('@docusaurus/utils');
|
||||||
|
|
||||||
module.exports = function loadConfig(siteDir) {
|
module.exports = async function loadTheme(siteDir) {
|
||||||
const customThemePath = path.resolve(siteDir, 'theme');
|
const themePath = path.resolve(siteDir, 'theme');
|
||||||
const themePath = fs.existsSync(customThemePath)
|
if (!fs.existsSync(themePath)) {
|
||||||
? customThemePath
|
return null;
|
||||||
: path.resolve(__dirname, '../../default-theme');
|
}
|
||||||
|
|
||||||
const requiredComponents = ['Loading', 'NotFound'];
|
const themeComponentFiles = await globby(['**/*.{js,jsx}'], {
|
||||||
requiredComponents.forEach(component => {
|
cwd: themePath,
|
||||||
try {
|
|
||||||
require.resolve(path.join(themePath, component));
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to load ${themePath}/${component}. It does not exist.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return themePath;
|
const alias = {};
|
||||||
|
await Promise.all(
|
||||||
|
themeComponentFiles.map(async relativeSource => {
|
||||||
|
const filePath = path.join(themePath, relativeSource);
|
||||||
|
const fileName = fileToPath(relativeSource);
|
||||||
|
const aliasName = posixPath(
|
||||||
|
normalizeUrl(['@theme', fileName]).replace(/\/$/, ''),
|
||||||
|
);
|
||||||
|
alias[aliasName] = filePath;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return alias;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,6 @@ const CSS_MODULE_REGEX = /\.module\.css$/;
|
||||||
module.exports = function createBaseConfig(props, isServer) {
|
module.exports = function createBaseConfig(props, isServer) {
|
||||||
const {
|
const {
|
||||||
outDir,
|
outDir,
|
||||||
themePath,
|
|
||||||
siteDir,
|
siteDir,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
|
@ -26,6 +25,7 @@ module.exports = function createBaseConfig(props, isServer) {
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
const themeFallback = path.resolve(__dirname, '../client/theme-fallback');
|
||||||
return {
|
return {
|
||||||
mode: isProd ? 'production' : 'development',
|
mode: isProd ? 'production' : 'development',
|
||||||
output: {
|
output: {
|
||||||
|
@ -42,10 +42,14 @@ module.exports = function createBaseConfig(props, isServer) {
|
||||||
resolve: {
|
resolve: {
|
||||||
symlinks: true,
|
symlinks: true,
|
||||||
alias: {
|
alias: {
|
||||||
|
// https://stackoverflow.com/a/55433680/6072730
|
||||||
ejs: 'ejs/ejs.min.js',
|
ejs: 'ejs/ejs.min.js',
|
||||||
'@theme': themePath,
|
// These alias can be overriden in plugins. However, these components are essential
|
||||||
|
// (e.g: react-loadable requires Loading component) so we alias it here first as fallback.
|
||||||
|
'@theme/Layout': path.join(themeFallback, 'Layout'),
|
||||||
|
'@theme/Loading': path.join(themeFallback, 'Loading'),
|
||||||
|
'@theme/NotFound': path.join(themeFallback, 'NotFound'),
|
||||||
'@site': siteDir,
|
'@site': siteDir,
|
||||||
'@build': outDir,
|
|
||||||
'@generated': generatedFilesDir,
|
'@generated': generatedFilesDir,
|
||||||
'@docusaurus': path.resolve(__dirname, '../client/exports'),
|
'@docusaurus': path.resolve(__dirname, '../client/exports'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
"clean-webpack-plugin": "^2.0.1",
|
"clean-webpack-plugin": "^2.0.1",
|
||||||
"commander": "^2.16.0",
|
"commander": "^2.16.0",
|
||||||
"css-loader": "^1.0.0",
|
"css-loader": "^1.0.0",
|
||||||
"docsearch.js": "^2.5.2",
|
|
||||||
"ejs": "^2.6.1",
|
"ejs": "^2.6.1",
|
||||||
"envinfo": "^7.2.0",
|
"envinfo": "^7.2.0",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
|
|
|
@ -66,7 +66,7 @@ function Home() {
|
||||||
|
|
||||||
// TODO: (wrapper function) API so that user won't need to concatenate url manually
|
// TODO: (wrapper function) API so that user won't need to concatenate url manually
|
||||||
const feedbackUrl = `${siteConfig.baseUrl}feedback/`;
|
const feedbackUrl = `${siteConfig.baseUrl}feedback/`;
|
||||||
const gettingStartedUrl = `${siteConfig.baseUrl}docs/installation`;
|
const gettingStartedUrl = `${siteConfig.baseUrl}docs/introduction`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Prefetch feedback pages & getting started pages
|
// Prefetch feedback pages & getting started pages
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue