mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-30 09:27:04 +02:00
feat(v2): easier plugin theme components swizzling (#1436)
* feat(v2): easier plugin theme components override * add context * refactor again * rename eject -> swizzle * nits
This commit is contained in:
parent
3298d8cd23
commit
0834784455
11 changed files with 91 additions and 90 deletions
|
@ -156,24 +156,12 @@ class DocusaurusPluginContentBlog {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getThemePath() {
|
||||||
|
return path.resolve(__dirname, './theme');
|
||||||
|
}
|
||||||
|
|
||||||
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: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -151,19 +151,12 @@ class DocusaurusPluginContentDocs {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getThemePath() {
|
||||||
|
return path.resolve(__dirname, './theme');
|
||||||
|
}
|
||||||
|
|
||||||
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: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,18 +19,8 @@ class DocusaurusThemeDefault {
|
||||||
return 'docusaurus-theme-classic';
|
return 'docusaurus-theme-classic';
|
||||||
}
|
}
|
||||||
|
|
||||||
configureWebpack() {
|
getThemePath() {
|
||||||
return {
|
return path.resolve(__dirname, './theme');
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@theme/Footer': path.resolve(__dirname, './theme/Footer'),
|
|
||||||
'@theme/Layout': path.resolve(__dirname, './theme/Layout'),
|
|
||||||
'@theme/Navbar': path.resolve(__dirname, './theme/Navbar'),
|
|
||||||
'@theme/NotFound': path.resolve(__dirname, './theme/NotFound'),
|
|
||||||
'@theme/Search': path.resolve(__dirname, './theme/Search'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ const envinfo = require('envinfo');
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const program = require('commander');
|
const program = require('commander');
|
||||||
const {build, eject, init, deploy, start} = require('../lib');
|
const {build, swizzle, init, deploy, start} = require('../lib');
|
||||||
const requiredVersion = require('../package.json').engines.node;
|
const requiredVersion = require('../package.json').engines.node;
|
||||||
|
|
||||||
if (!semver.satisfies(process.version, requiredVersion)) {
|
if (!semver.satisfies(process.version, requiredVersion)) {
|
||||||
|
@ -55,10 +55,10 @@ program
|
||||||
});
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('eject [siteDir]')
|
.command('swizzle <themeName> [componentName] [siteDir]')
|
||||||
.description('copy the default theme into website folder for customization.')
|
.description('Copy the theme files into website folder for customization.')
|
||||||
.action((siteDir = '.') => {
|
.action((themeName, componentName, siteDir = '.') => {
|
||||||
wrapCommand(eject)(path.resolve(siteDir));
|
wrapCommand(swizzle)(path.resolve(siteDir), themeName, componentName);
|
||||||
});
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|
|
@ -1,23 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = async function eject(siteDir) {
|
|
||||||
const defaultTheme = path.resolve(__dirname, '..', 'default-theme');
|
|
||||||
const customTheme = path.resolve(siteDir, 'theme');
|
|
||||||
await fs.copy(defaultTheme, customTheme);
|
|
||||||
|
|
||||||
const relativeDir = path.relative(process.cwd(), customTheme);
|
|
||||||
console.log(
|
|
||||||
`\n${chalk.green('Success!')} Copied default theme files to ${chalk.cyan(
|
|
||||||
relativeDir,
|
|
||||||
)}.\n`,
|
|
||||||
);
|
|
||||||
};
|
|
36
packages/docusaurus/lib/commands/swizzle.js
Normal file
36
packages/docusaurus/lib/commands/swizzle.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 fs = require('fs-extra');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const path = require('path');
|
||||||
|
const importFresh = require('import-fresh');
|
||||||
|
|
||||||
|
module.exports = async function swizzle(siteDir, themeName, componentName) {
|
||||||
|
const Plugin = importFresh(themeName);
|
||||||
|
const context = {siteDir};
|
||||||
|
const PluginInstance = new Plugin(context);
|
||||||
|
let fromPath = PluginInstance.getThemePath();
|
||||||
|
|
||||||
|
if (fromPath) {
|
||||||
|
let toPath = path.resolve(siteDir, 'theme');
|
||||||
|
if (componentName) {
|
||||||
|
fromPath = path.join(fromPath, componentName);
|
||||||
|
toPath = path.join(toPath, componentName);
|
||||||
|
}
|
||||||
|
await fs.copy(fromPath, toPath);
|
||||||
|
|
||||||
|
const relativeDir = path.relative(process.cwd(), toPath);
|
||||||
|
const fromMsg = chalk.blue(
|
||||||
|
componentName ? `${themeName}/${componentName}` : themeName,
|
||||||
|
);
|
||||||
|
const toMsg = chalk.cyan(relativeDir);
|
||||||
|
console.log(
|
||||||
|
`\n${chalk.green('Success!')} Copied ${fromMsg} to ${toMsg}.\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -8,12 +8,12 @@
|
||||||
const build = require('./commands/build');
|
const build = require('./commands/build');
|
||||||
const init = require('./commands/init');
|
const init = require('./commands/init');
|
||||||
const start = require('./commands/start');
|
const start = require('./commands/start');
|
||||||
const eject = require('./commands/eject');
|
const swizzle = require('./commands/swizzle');
|
||||||
const deploy = require('./commands/deploy');
|
const deploy = require('./commands/deploy');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
build,
|
build,
|
||||||
eject,
|
swizzle,
|
||||||
init,
|
init,
|
||||||
start,
|
start,
|
||||||
deploy,
|
deploy,
|
||||||
|
|
|
@ -51,10 +51,35 @@ 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 custom theme override aliases.
|
// Default theme components that are essential and must exist in a Docusaurus app
|
||||||
const themeAliases = await loadTheme(siteDir);
|
// These can be overriden in plugins/ through component swizzling.
|
||||||
// Make a fake plugin to resolve user's theme overrides.
|
// However, we alias it here first as a fallback.
|
||||||
if (themeAliases != null) {
|
const themeFallback = path.resolve(__dirname, '../client/theme-fallback');
|
||||||
|
let themeAliases = await loadTheme(themeFallback);
|
||||||
|
|
||||||
|
// create theme alias from plugins
|
||||||
|
await Promise.all(
|
||||||
|
plugins.map(async plugin => {
|
||||||
|
if (!plugin.getThemePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const aliases = await loadTheme(plugin.getThemePath());
|
||||||
|
themeAliases = {
|
||||||
|
...themeAliases,
|
||||||
|
...aliases,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// user's own theme alias override. Highest priority
|
||||||
|
const themePath = path.resolve(siteDir, 'theme');
|
||||||
|
const aliases = await loadTheme(themePath);
|
||||||
|
themeAliases = {
|
||||||
|
...themeAliases,
|
||||||
|
...aliases,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a fake plugin to resolve alias theme.
|
||||||
plugins.push({
|
plugins.push({
|
||||||
configureWebpack: () => ({
|
configureWebpack: () => ({
|
||||||
resolve: {
|
resolve: {
|
||||||
|
@ -62,7 +87,6 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -10,8 +10,7 @@ const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const {fileToPath, posixPath, normalizeUrl} = require('@docusaurus/utils');
|
const {fileToPath, posixPath, normalizeUrl} = require('@docusaurus/utils');
|
||||||
|
|
||||||
module.exports = async function loadTheme(siteDir) {
|
module.exports = async function loadTheme(themePath) {
|
||||||
const themePath = path.resolve(siteDir, 'theme');
|
|
||||||
if (!fs.existsSync(themePath)) {
|
if (!fs.existsSync(themePath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ 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: {
|
||||||
|
@ -44,11 +43,6 @@ module.exports = function createBaseConfig(props, isServer) {
|
||||||
alias: {
|
alias: {
|
||||||
// https://stackoverflow.com/a/55433680/6072730
|
// https://stackoverflow.com/a/55433680/6072730
|
||||||
ejs: 'ejs/ejs.min.js',
|
ejs: 'ejs/ejs.min.js',
|
||||||
// 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,
|
||||||
'@generated': generatedFilesDir,
|
'@generated': generatedFilesDir,
|
||||||
'@docusaurus': path.resolve(__dirname, '../client/exports'),
|
'@docusaurus': path.resolve(__dirname, '../client/exports'),
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "docusaurus start",
|
"start": "docusaurus start",
|
||||||
"build": "docusaurus build",
|
"build": "docusaurus build",
|
||||||
"eject": "docusaurus eject",
|
"swizzle": "docusaurus swizzle",
|
||||||
"deploy": "docusaurus deploy"
|
"deploy": "docusaurus deploy"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue