mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 08:07:26 +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"
|
||||
},
|
||||
"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}) {
|
||||
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: {
|
||||
rules: [
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
import BlogPostItem from '../BlogPostItem';
|
||||
import BlogPostItem from '@theme/BlogPostItem';
|
||||
|
||||
function BlogListPage(props) {
|
||||
const {
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
import BlogPostItem from '../BlogPostItem';
|
||||
import BlogPostItem from '@theme/BlogPostItem';
|
||||
|
||||
function BlogPostPage(props) {
|
||||
const {content: BlogPostContents, metadata} = props;
|
|
@ -18,6 +18,12 @@
|
|||
"loader-utils": "^1.2.3"
|
||||
},
|
||||
"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}) {
|
||||
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: {
|
||||
rules: [
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
|
||||
import Head from '@docusaurus/Head';
|
||||
|
||||
import DocPaginator from '../DocPaginator';
|
||||
import DocPaginator from '@theme/DocPaginator';
|
||||
|
||||
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 DocSidebar from '../DocSidebar';
|
||||
import DocSidebar from '@theme/DocSidebar';
|
||||
|
||||
function DocPage(props) {
|
||||
const {route, docsMetadata, location} = props;
|
|
@ -12,6 +12,11 @@
|
|||
"globby": "^9.1.0"
|
||||
},
|
||||
"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 = {}) {
|
||||
return {
|
||||
themes: [
|
||||
{
|
||||
name: '@docusaurus/theme-classic',
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
{
|
||||
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]);
|
||||
|
||||
// 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};
|
||||
|
||||
// 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({
|
||||
pluginConfigs,
|
||||
context,
|
||||
|
@ -43,8 +51,18 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
|||
const outDir = path.resolve(siteDir, 'build');
|
||||
const {baseUrl} = siteConfig;
|
||||
|
||||
// Resolve theme. TBD (Experimental)
|
||||
const themePath = loadTheme(siteDir);
|
||||
// Resolve custom theme override aliases.
|
||||
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
|
||||
const {
|
||||
|
@ -81,7 +99,6 @@ ${Object.keys(registry)
|
|||
siteConfig,
|
||||
siteDir,
|
||||
outDir,
|
||||
themePath,
|
||||
baseUrl,
|
||||
generatedFilesDir,
|
||||
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', () => {
|
||||
test('no presets', () => {
|
||||
const presets = loadPresets({siteConfig: {presets: []}});
|
||||
expect(presets).toEqual([]);
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"plugins": Array [],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('string form', () => {
|
||||
|
@ -23,7 +28,8 @@ describe('loadPresets', () => {
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": undefined,
|
||||
|
@ -32,7 +38,9 @@ Array [
|
|||
"name": "@docusaurus/plugin-content-blog",
|
||||
"options": undefined,
|
||||
},
|
||||
]
|
||||
],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -46,7 +54,8 @@ Array [
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": undefined,
|
||||
|
@ -63,7 +72,9 @@ Array [
|
|||
"name": "@docusaurus/plugin-sitemap",
|
||||
"options": undefined,
|
||||
},
|
||||
]
|
||||
],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -74,7 +85,8 @@ Array [
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": undefined,
|
||||
|
@ -83,7 +95,9 @@ Array [
|
|||
"name": "@docusaurus/plugin-content-blog",
|
||||
"options": undefined,
|
||||
},
|
||||
]
|
||||
],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -99,7 +113,8 @@ Array [
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": Object {
|
||||
|
@ -110,7 +125,9 @@ Array [
|
|||
"name": "@docusaurus/plugin-content-blog",
|
||||
"options": undefined,
|
||||
},
|
||||
]
|
||||
],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -130,7 +147,8 @@ Array [
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": Object {
|
||||
|
@ -151,7 +169,9 @@ Array [
|
|||
"name": "@docusaurus/plugin-sitemap",
|
||||
"options": undefined,
|
||||
},
|
||||
]
|
||||
],
|
||||
"themes": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -168,7 +188,8 @@ Array [
|
|||
},
|
||||
});
|
||||
expect(presets).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": Object {
|
||||
|
@ -187,7 +208,58 @@ Array [
|
|||
"name": "@docusaurus/plugin-sitemap",
|
||||
"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,12 +7,13 @@
|
|||
|
||||
const importFresh = require('import-fresh');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
module.exports = function loadPresets(context) {
|
||||
const presets = context.siteConfig.presets || [];
|
||||
return _.flatten(
|
||||
presets.map(presetItem => {
|
||||
const plugins = [];
|
||||
const themes = [];
|
||||
|
||||
presets.forEach(presetItem => {
|
||||
let presetModule;
|
||||
let presetOptions = {};
|
||||
if (typeof presetItem === 'string') {
|
||||
|
@ -21,15 +22,13 @@ module.exports = function loadPresets(context) {
|
|||
[presetModule, presetOptions] = presetItem;
|
||||
}
|
||||
|
||||
let preset;
|
||||
if (presetModule && fs.existsSync(presetModule)) {
|
||||
// Local preset.
|
||||
preset = importFresh(presetModule);
|
||||
} else {
|
||||
// From npm.
|
||||
preset = importFresh(presetModule);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
||||
const globby = require('globby');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const {fileToPath, posixPath, normalizeUrl} = require('@docusaurus/utils');
|
||||
|
||||
module.exports = function loadConfig(siteDir) {
|
||||
const customThemePath = path.resolve(siteDir, 'theme');
|
||||
const themePath = fs.existsSync(customThemePath)
|
||||
? customThemePath
|
||||
: path.resolve(__dirname, '../../default-theme');
|
||||
|
||||
const requiredComponents = ['Loading', 'NotFound'];
|
||||
requiredComponents.forEach(component => {
|
||||
try {
|
||||
require.resolve(path.join(themePath, component));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to load ${themePath}/${component}. It does not exist.`,
|
||||
);
|
||||
module.exports = async function loadTheme(siteDir) {
|
||||
const themePath = path.resolve(siteDir, 'theme');
|
||||
if (!fs.existsSync(themePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const themeComponentFiles = await globby(['**/*.{js,jsx}'], {
|
||||
cwd: themePath,
|
||||
});
|
||||
|
||||
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) {
|
||||
const {
|
||||
outDir,
|
||||
themePath,
|
||||
siteDir,
|
||||
baseUrl,
|
||||
generatedFilesDir,
|
||||
|
@ -26,6 +25,7 @@ module.exports = function createBaseConfig(props, isServer) {
|
|||
} = props;
|
||||
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const themeFallback = path.resolve(__dirname, '../client/theme-fallback');
|
||||
return {
|
||||
mode: isProd ? 'production' : 'development',
|
||||
output: {
|
||||
|
@ -42,10 +42,14 @@ module.exports = function createBaseConfig(props, isServer) {
|
|||
resolve: {
|
||||
symlinks: true,
|
||||
alias: {
|
||||
// https://stackoverflow.com/a/55433680/6072730
|
||||
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,
|
||||
'@build': outDir,
|
||||
'@generated': generatedFilesDir,
|
||||
'@docusaurus': path.resolve(__dirname, '../client/exports'),
|
||||
},
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
"clean-webpack-plugin": "^2.0.1",
|
||||
"commander": "^2.16.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"docsearch.js": "^2.5.2",
|
||||
"ejs": "^2.6.1",
|
||||
"envinfo": "^7.2.0",
|
||||
"express": "^4.16.4",
|
||||
|
|
|
@ -66,7 +66,7 @@ function Home() {
|
|||
|
||||
// TODO: (wrapper function) API so that user won't need to concatenate url manually
|
||||
const feedbackUrl = `${siteConfig.baseUrl}feedback/`;
|
||||
const gettingStartedUrl = `${siteConfig.baseUrl}docs/installation`;
|
||||
const gettingStartedUrl = `${siteConfig.baseUrl}docs/introduction`;
|
||||
|
||||
useEffect(() => {
|
||||
// Prefetch feedback pages & getting started pages
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue