Skip to content

Commit

Permalink
feat: shorthands for themes/plugins/presets configuration (#5930)
Browse files Browse the repository at this point in the history
Co-authored-by: Josh-Cena <sidachen2003@gmail.com>
Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
  • Loading branch information
3 people committed Dec 3, 2021
1 parent bfd7fd9 commit b366ba5
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const config = {

presets: [
[
'@docusaurus/preset-classic',
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const config = {

presets: [
[
'@docusaurus/preset-classic',
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
Expand Down
8 changes: 8 additions & 0 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ export interface Preset {
themes?: PluginConfig[];
}

export type PresetModule = {
<T>(context: LoadContext, presetOptions: T): Preset;
};

export type ImportedPresetModule = PresetModule & {
default?: PresetModule;
};

export type PresetConfig =
| [string, Record<string, unknown>]
| [string]
Expand Down
90 changes: 90 additions & 0 deletions packages/docusaurus/src/server/__tests__/moduleShorthand.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* 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 {getNamePatterns, resolveModuleName} from '../moduleShorthand';

describe('getNamePatterns', () => {
test('should resolve plain names', () => {
expect(getNamePatterns('awesome', 'plugin')).toEqual([
'awesome',
'@docusaurus/plugin-awesome',
'docusaurus-plugin-awesome',
]);

expect(getNamePatterns('awesome', 'theme')).toEqual([
'awesome',
'@docusaurus/theme-awesome',
'docusaurus-theme-awesome',
]);
});

test('should expand bare scopes', () => {
expect(getNamePatterns('@joshcena', 'plugin')).toEqual([
'@joshcena/docusaurus-plugin',
]);

expect(getNamePatterns('@joshcena', 'theme')).toEqual([
'@joshcena/docusaurus-theme',
]);
});

test('should expand scoped names', () => {
expect(getNamePatterns('@joshcena/awesome', 'plugin')).toEqual([
'@joshcena/awesome',
'@joshcena/docusaurus-plugin-awesome',
]);

expect(getNamePatterns('@joshcena/awesome', 'theme')).toEqual([
'@joshcena/awesome',
'@joshcena/docusaurus-theme-awesome',
]);
});

test('should expand deep scoped paths', () => {
expect(getNamePatterns('@joshcena/awesome/web', 'plugin')).toEqual([
'@joshcena/awesome/web',
'@joshcena/docusaurus-plugin-awesome/web',
]);

expect(getNamePatterns('@joshcena/awesome/web', 'theme')).toEqual([
'@joshcena/awesome/web',
'@joshcena/docusaurus-theme-awesome/web',
]);
});
});

describe('resolveModuleName', () => {
test('should resolve longhand', () => {
expect(
resolveModuleName('@docusaurus/plugin-content-docs', require, 'plugin'),
).toBeDefined();
});

test('should resolve shorthand', () => {
expect(resolveModuleName('content-docs', require, 'plugin')).toBeDefined();
});

test('should throw good error message for longhand', () => {
expect(() =>
resolveModuleName('@docusaurus/plugin-content-doc', require, 'plugin'),
).toThrowErrorMatchingInlineSnapshot(`
"Docusaurus was unable to resolve the \\"@docusaurus/plugin-content-doc\\" plugin. Make sure one of the following packages are installed:
- @docusaurus/plugin-content-doc
- @docusaurus/docusaurus-plugin-plugin-content-doc"
`);
});

test('should throw good error message for shorthand', () => {
expect(() => resolveModuleName('content-doc', require, 'plugin'))
.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus was unable to resolve the \\"content-doc\\" plugin. Make sure one of the following packages are installed:
- content-doc
- @docusaurus/plugin-content-doc
- docusaurus-plugin-content-doc"
`);
});
});
40 changes: 36 additions & 4 deletions packages/docusaurus/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
import {mapValues} from 'lodash';
import {RuleSetRule} from 'webpack';
import admonitions from 'remark-admonitions';
import {createRequire} from 'module';
import {resolveModuleName} from './moduleShorthand';

export type LoadContextOptions = {
customOutDir?: string;
Expand Down Expand Up @@ -127,14 +129,44 @@ export async function loadContext(
}

export function loadPluginConfigs(context: LoadContext): PluginConfig[] {
const {plugins: presetPlugins, themes: presetThemes} = loadPresets(context);
const {siteConfig} = context;
let {plugins: presetPlugins, themes: presetThemes} = loadPresets(context);
const {siteConfig, siteConfigPath} = context;
const require = createRequire(siteConfigPath);
function normalizeShorthand(
pluginConfig: PluginConfig,
pluginType: 'plugin' | 'theme',
): PluginConfig {
if (typeof pluginConfig === 'string') {
return resolveModuleName(pluginConfig, require, pluginType);
} else if (
Array.isArray(pluginConfig) &&
typeof pluginConfig[0] === 'string'
) {
return [
resolveModuleName(pluginConfig[0], require, pluginType),
pluginConfig[1] ?? {},
];
}
return pluginConfig;
}
presetPlugins = presetPlugins.map((plugin) =>
normalizeShorthand(plugin, 'plugin'),
);
presetThemes = presetThemes.map((theme) =>
normalizeShorthand(theme, 'theme'),
);
const standalonePlugins = (siteConfig.plugins || []).map((plugin) =>
normalizeShorthand(plugin, 'plugin'),
);
const standaloneThemes = (siteConfig.themes || []).map((theme) =>
normalizeShorthand(theme, 'theme'),
);
return [
...presetPlugins,
...presetThemes,
// Site config should be the highest priority.
...(siteConfig.plugins || []),
...(siteConfig.themes || []),
...standalonePlugins,
...standaloneThemes,
];
}

Expand Down
45 changes: 45 additions & 0 deletions packages/docusaurus/src/server/moduleShorthand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* 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.
*/

export function getNamePatterns(
moduleName: string,
moduleType: 'preset' | 'theme' | 'plugin',
): string[] {
if (moduleName.startsWith('@')) {
// Pure scope: `@scope` => `@scope/docusaurus-plugin`
if (!moduleName.includes('/')) {
return [`${moduleName}/docusaurus-${moduleType}`];
}
const [scope, packageName] = moduleName.split(/\/(.*)/);
return [
`${scope}/${packageName}`,
`${scope}/docusaurus-${moduleType}-${packageName}`,
];
}
return [
moduleName,
`@docusaurus/${moduleType}-${moduleName}`,
`docusaurus-${moduleType}-${moduleName}`,
];
}

export function resolveModuleName(
moduleName: string,
moduleRequire: NodeRequire,
moduleType: 'preset' | 'theme' | 'plugin',
): string {
const modulePatterns = getNamePatterns(moduleName, moduleType);
// eslint-disable-next-line no-restricted-syntax
for (const module of modulePatterns) {
try {
moduleRequire.resolve(module);
return module;
} catch (e) {}
}
throw new Error(`Docusaurus was unable to resolve the "${moduleName}" ${moduleType}. Make sure one of the following packages are installed:
${modulePatterns.map((module) => `- ${module}`).join('\n')}`);
}
32 changes: 16 additions & 16 deletions packages/docusaurus/src/server/presets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import importFresh from 'import-fresh';
import {
LoadContext,
PluginConfig,
Preset,
PresetConfig,
ImportedPresetModule,
} from '@docusaurus/types';
import {resolveModuleName} from '../moduleShorthand';

export default function loadPresets(context: LoadContext): {
plugins: PluginConfig[];
themes: PluginConfig[];
} {
// We need to resolve plugins from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these plugins.
const pluginRequire = createRequire(context.siteConfigPath);
// We need to resolve presets from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these presets.
const presetRequire = createRequire(context.siteConfigPath);

const presets: PresetConfig[] = (context.siteConfig || {}).presets || [];
const unflatPlugins: PluginConfig[][] = [];
Expand All @@ -36,17 +37,16 @@ export default function loadPresets(context: LoadContext): {
} else {
throw new Error('Invalid presets format detected in config.');
}
const presetName = resolveModuleName(
presetModuleImport,
presetRequire,
'preset',
);

type PresetInitializeFunction = (
context: LoadContext,
presetOptions: Record<string, unknown>,
) => Preset;
const presetModule = importFresh<
PresetInitializeFunction & {
default?: PresetInitializeFunction;
}
>(pluginRequire.resolve(presetModuleImport));
const preset: Preset = (presetModule.default || presetModule)(
const presetModule = importFresh<ImportedPresetModule>(
presetRequire.resolve(presetName),
);
const preset = (presetModule.default ?? presetModule)(
context,
presetOptions,
);
Expand All @@ -60,7 +60,7 @@ export default function loadPresets(context: LoadContext): {
});

return {
plugins: ([] as PluginConfig[]).concat(...unflatPlugins).filter(Boolean),
themes: ([] as PluginConfig[]).concat(...unflatThemes).filter(Boolean),
plugins: unflatPlugins.flat().filter(Boolean),
themes: unflatThemes.flat().filter(Boolean),
};
}
6 changes: 3 additions & 3 deletions website/_dogfooding/dogfooding.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const fs = require('fs');
/** @type {import('@docusaurus/types').PluginConfig[]} */
const dogfoodingPluginInstances = [
[
'@docusaurus/plugin-content-docs',
'content-docs', // dogfood shorthand
/** @type {import('@docusaurus/plugin-content-docs').Options} */
({
id: 'docs-tests',
Expand All @@ -24,7 +24,7 @@ const dogfoodingPluginInstances = [
],

[
'@docusaurus/plugin-content-blog',
'@docusaurus/plugin-content-blog', // dogfood longhand
/** @type {import('@docusaurus/plugin-content-blog').Options} */
({
id: 'blog-tests',
Expand All @@ -46,7 +46,7 @@ const dogfoodingPluginInstances = [
],

[
'@docusaurus/plugin-content-pages',
require.resolve('@docusaurus/plugin-content-pages'), // dogfood longhand resolve
/** @type {import('@docusaurus/plugin-content-pages').Options} */
({
id: 'pages-tests',
Expand Down
Loading

0 comments on commit b366ba5

Please sign in to comment.