Skip to content

Commit fdb0406

Browse files
authored
feat(doc-core): add routes for addPages hook (#3753)
* feat: add routes for `addPages` hook * chore: changeset
1 parent 5c56949 commit fdb0406

File tree

20 files changed

+322
-270
lines changed

20 files changed

+322
-270
lines changed

.changeset/gorgeous-suns-return.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modern-js/doc-core': patch
3+
---
4+
5+
feat(doc-core): add routes for addPages hook
6+
7+
feat(doc-core): 在 addPages 钩子中增加路由入参
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { UserConfig, PageIndexInfo, DocPlugin, RouteMeta } from 'shared/types';
2+
3+
export class PluginDriver {
4+
#config: UserConfig;
5+
6+
#plugins: DocPlugin[];
7+
8+
#isProd: boolean;
9+
10+
constructor(config: UserConfig, isProd: boolean) {
11+
this.#config = config;
12+
this.#isProd = isProd;
13+
}
14+
15+
// The init function is used to initialize the doc plugins and will execute before the build process.
16+
async init() {
17+
// Clear docPlugins first, for the watch mode
18+
this.clearPlugins();
19+
const config = this.#config;
20+
const enableLastUpdated =
21+
config.doc.themeConfig?.lastUpdated ||
22+
config.doc.themeConfig?.locales?.some(locale => locale.lastUpdated);
23+
const mediumZoomConfig = config.doc.mediumZoom ?? true;
24+
if (enableLastUpdated) {
25+
const { pluginLastUpdated } = await import('./plugins/lastUpdated');
26+
this.addPlugin(pluginLastUpdated());
27+
}
28+
if (mediumZoomConfig) {
29+
const { pluginMediumZoom } = await import(
30+
'@modern-js/doc-plugin-medium-zoom'
31+
);
32+
this.addPlugin(
33+
pluginMediumZoom(
34+
typeof mediumZoomConfig === 'object' ? mediumZoomConfig : undefined,
35+
),
36+
);
37+
}
38+
(config.doc.plugins || []).forEach(plugin => {
39+
this.addPlugin(plugin);
40+
});
41+
}
42+
43+
addPlugin(plugin: DocPlugin) {
44+
const exsitedIndex = this.#plugins.findIndex(
45+
item => item.name === plugin.name,
46+
);
47+
// Avoid the duplicated plugin
48+
if (exsitedIndex !== -1) {
49+
throw new Error(`The plugin "${plugin.name}" has been registered`);
50+
} else {
51+
this.#plugins.push(plugin);
52+
}
53+
}
54+
55+
clearPlugins() {
56+
this.#plugins = [];
57+
}
58+
59+
async modifyConfig() {
60+
let config = this.#config.doc;
61+
62+
for (const plugin of this.#plugins) {
63+
if (typeof plugin.config === 'function') {
64+
config = await plugin.config(config || {});
65+
}
66+
}
67+
this.#config.doc = config;
68+
return this.#config;
69+
}
70+
71+
async beforeBuild() {
72+
return await Promise.all(
73+
this.#plugins
74+
.filter(plugin => typeof plugin.beforeBuild === 'function')
75+
.map(plugin => {
76+
return plugin.beforeBuild(this.#config.doc || {}, this.#isProd);
77+
}),
78+
);
79+
}
80+
81+
async afterBuild() {
82+
return await Promise.all(
83+
this.#plugins
84+
.filter(plugin => typeof plugin.afterBuild === 'function')
85+
.map(plugin => {
86+
return plugin.afterBuild(this.#config.doc || {}, this.#isProd);
87+
}),
88+
);
89+
}
90+
91+
async extendPageData(pageData: PageIndexInfo) {
92+
await Promise.all(
93+
this.#plugins
94+
.filter(plugin => typeof plugin.extendPageData === 'function')
95+
.map(plugin => {
96+
return plugin.extendPageData(pageData);
97+
}),
98+
);
99+
}
100+
101+
async addPages(routes: RouteMeta[]) {
102+
// addPages hooks
103+
const result = await Promise.all(
104+
this.#plugins
105+
.filter(plugin => typeof plugin.addPages === 'function')
106+
.map(plugin => {
107+
return plugin.addPages(this.#config.doc || {}, this.#isProd, routes);
108+
}),
109+
);
110+
111+
return result.flat();
112+
}
113+
114+
globalUIComponents(): string[] {
115+
const result = this.#plugins.map(plugin => {
116+
return plugin.globalUIComponents || [];
117+
});
118+
119+
return result.flat();
120+
}
121+
122+
globalStyles(): string[] {
123+
return this.#plugins
124+
.filter(plugin => typeof plugin.globalStyles === 'string')
125+
.map(plugin => {
126+
return plugin.globalStyles;
127+
});
128+
}
129+
}

packages/cli/doc-core/src/node/build.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import {
1212
} from './constants';
1313
import { createModernBuilder } from './createBuilder';
1414
import { writeSearchIndex } from './searchIndex';
15-
import { modifyConfig, beforeBuild, afterBuild, loadPlugins } from './hooks';
1615
import { logger } from './utils';
16+
import { PluginDriver } from './PluginDriver';
1717
import { APPEARANCE_KEY, normalizeSlash } from '@/shared/utils';
1818
import type { Route } from '@/node/route/RouteService';
1919

@@ -30,11 +30,15 @@ const CHECK_DARK_LIGHT_SCRIPT = `
3030
</script>
3131
`;
3232

33-
export async function bundle(rootDir: string, config: UserConfig) {
33+
export async function bundle(
34+
rootDir: string,
35+
config: UserConfig,
36+
pluginDriver: PluginDriver,
37+
) {
3438
try {
3539
const [clientBuilder, ssrBuilder] = await Promise.all([
36-
createModernBuilder(rootDir, config, false),
37-
createModernBuilder(rootDir, config, true, {
40+
createModernBuilder(rootDir, config, pluginDriver, false),
41+
createModernBuilder(rootDir, config, pluginDriver, true, {
3842
output: {
3943
distPath: {
4044
root: `${config.doc?.outDir ?? OUTPUT_DIR}/ssr`,
@@ -136,21 +140,11 @@ export async function renderPages(config: UserConfig) {
136140
}
137141

138142
export async function build(rootDir: string, config: UserConfig) {
139-
const isProd = true;
140-
await loadPlugins(config);
141-
142-
const modifiedConfig = await modifyConfig({
143-
config,
144-
});
145-
146-
await beforeBuild({
147-
config: modifiedConfig,
148-
isProd,
149-
});
150-
await bundle(rootDir, modifiedConfig);
143+
const pluginDriver = new PluginDriver(config, true);
144+
await pluginDriver.init();
145+
const modifiedConfig = await pluginDriver.modifyConfig();
146+
await pluginDriver.beforeBuild();
147+
await bundle(rootDir, modifiedConfig, pluginDriver);
151148
await renderPages(modifiedConfig);
152-
await afterBuild({
153-
config: modifiedConfig,
154-
isProd,
155-
});
149+
await pluginDriver.afterBuild();
156150
}

packages/cli/doc-core/src/node/createBuilder.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { builderDocVMPlugin, runtimeModuleIDs } from './runtimeModule';
2323
import { serveSearchIndexMiddleware } from './searchIndex';
2424
import { checkLinks } from './mdx/remarkPlugins/checkDeadLink';
2525
import { detectReactVersion, resolveReactAlias } from './utils';
26+
import { initRouteService } from './route/init';
27+
import { PluginDriver } from './PluginDriver';
2628

2729
const require = createRequire(import.meta.url);
2830

@@ -184,6 +186,7 @@ async function createInternalBuildConfig(
184186
export async function createModernBuilder(
185187
rootDir: string,
186188
config: UserConfig,
189+
pluginDriver: PluginDriver,
187190
isSSR = false,
188191
extraBuilderConfig?: BuilderConfig,
189192
): Promise<BuilderInstance<BuilderRspackProvider>> {
@@ -194,9 +197,13 @@ export async function createModernBuilder(
194197
TEMP_DIR,
195198
isSSR ? 'ssr-runtime' : 'client-runtime',
196199
);
197-
198200
await fs.ensureDir(runtimeTempDir);
199-
201+
const routeService = await initRouteService({
202+
config,
203+
runtimeTempDir,
204+
scanDir: userRoot,
205+
pluginDriver,
206+
});
200207
const { createBuilder } = await import('@modern-js/builder');
201208
const { builderRspackProvider } = await import(
202209
'@modern-js/builder-rspack-provider'
@@ -226,7 +233,14 @@ export async function createModernBuilder(
226233
});
227234

228235
builder.addPlugins([
229-
builderDocVMPlugin(userRoot, config, isSSR, runtimeTempDir),
236+
builderDocVMPlugin({
237+
userRoot,
238+
config,
239+
isSSR,
240+
runtimeTempDir,
241+
routeService,
242+
pluginDriver,
243+
}),
230244
]);
231245

232246
return builder;

packages/cli/doc-core/src/node/dev.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { UserConfig } from 'shared/types';
22
import { removeLeadingSlash } from '../shared/utils';
33
import { createModernBuilder } from './createBuilder';
44
import { writeSearchIndex } from './searchIndex';
5-
import { modifyConfig, beforeBuild, afterBuild, loadPlugins } from './hooks';
5+
import { PluginDriver } from './PluginDriver';
66

77
interface ServerInstance {
88
close: () => Promise<void>;
@@ -14,16 +14,19 @@ export async function dev(
1414
): Promise<ServerInstance> {
1515
const base = config.doc?.base ?? '';
1616
const isProd = false;
17-
await loadPlugins(config);
17+
const pluginDriver = new PluginDriver(config, isProd);
18+
await pluginDriver.init();
19+
1820
try {
19-
const modifiedConfig = await modifyConfig({
20-
config,
21-
});
22-
await beforeBuild({
23-
config: modifiedConfig,
24-
isProd,
25-
});
26-
const builder = await createModernBuilder(rootDir, modifiedConfig);
21+
const modifiedConfig = await pluginDriver.modifyConfig();
22+
await pluginDriver.beforeBuild();
23+
const builder = await createModernBuilder(
24+
rootDir,
25+
modifiedConfig,
26+
pluginDriver,
27+
false,
28+
{},
29+
);
2730
const { server } = await builder.startDevServer({
2831
printURLs: urls => {
2932
return urls.map(({ label, url }) => ({
@@ -33,10 +36,7 @@ export async function dev(
3336
},
3437
});
3538

36-
await afterBuild({
37-
config: modifiedConfig,
38-
isProd,
39-
});
39+
await pluginDriver.afterBuild();
4040
return server;
4141
} finally {
4242
await writeSearchIndex(config);

0 commit comments

Comments
 (0)