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:
Endi 2019-05-07 13:56:43 +07:00 committed by GitHub
parent 3298d8cd23
commit 0834784455
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 91 additions and 90 deletions

View file

@ -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: [
{ {

View file

@ -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: [
{ {

View file

@ -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'),
},
},
};
} }
} }

View file

@ -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

View file

@ -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`,
);
};

View 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`,
);
}
};

View file

@ -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,

View file

@ -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 {

View file

@ -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;
} }

View file

@ -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'),

View file

@ -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": {