Skip to content

Commit 48c11bf

Browse files
authored
feat: export router extend hooks type (#6750)
1 parent 0e5b623 commit 48c11bf

File tree

12 files changed

+193
-102
lines changed

12 files changed

+193
-102
lines changed

.changeset/plenty-donuts-call.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@modern-js/plugin-router-v5': patch
3+
'@modern-js/runtime': patch
4+
---
5+
6+
feat: export router extend hooks type
7+
8+
feat: 导出 router 插件扩展 Hooks 类型

.changeset/silly-ways-smile.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modern-js/plugin-v2': patch
3+
---
4+
5+
feat: enhance plugin API type inference, supporting extendsHooks and extendsAPI
6+
7+
feat: 增强 plugin api 类型推断,支持 extendsHooks 和 extendsAPI

packages/runtime/plugin-router-v5/src/runtime/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { routerPlugin } from './plugin';
22

3-
export type { SingleRouteConfig, HistoryConfig, RouterConfig } from './plugin';
3+
export type {
4+
SingleRouteConfig,
5+
HistoryConfig,
6+
RouterConfig,
7+
RouterExtendsHooks,
8+
} from './plugin';
49
export { routerPlugin } from './plugin';
510
export default routerPlugin;
611

packages/runtime/plugin-router-v5/src/runtime/plugin.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import {
2222
import { modifyRoutesHook } from './hooks';
2323
import { getLocation, renderRoutes, urlJoin } from './utils';
2424

25+
export type RouterExtendsHooks = {
26+
modifyRoutes: typeof modifyRoutesHook;
27+
};
28+
2529
export type SingleRouteConfig = RouteProps & {
2630
redirect?: string;
2731
routes?: SingleRouteConfig[];
@@ -64,9 +68,7 @@ let routes: SingleRouteConfig[] = [];
6468
export const routerPlugin = (
6569
userConfig: RouterConfig = {},
6670
): RuntimePluginFuture<{
67-
extendHooks: {
68-
modifyRoutes: typeof modifyRoutesHook;
69-
};
71+
extendHooks: RouterExtendsHooks;
7072
}> => {
7173
return {
7274
name: '@modern-js/plugin-router',

packages/runtime/plugin-runtime/src/router/runtime/hooks.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ const onBeforeCreateRoutes =
88
createSyncHook<(context: RuntimeContext) => void>();
99

1010
export { modifyRoutes, onBeforeCreateRoutes };
11+
12+
export type RouterExtendsHooks = {
13+
modifyRoutes: typeof modifyRoutes;
14+
onBeforeCreateRoutes: typeof onBeforeCreateRoutes;
15+
};

packages/runtime/plugin-runtime/src/router/runtime/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { routerPlugin };
1111
export default routerPlugin;
1212

1313
export { modifyRoutes } from './plugin';
14+
export type { RouterExtendsHooks } from './hooks';
1415

1516
export * from './withRouter';
1617

packages/runtime/plugin-runtime/src/router/runtime/plugin.node.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { RuntimePluginFuture } from '../../core';
1919
import { getGlobalLayoutApp, getGlobalRoutes } from '../../core/context';
2020
import DeferredDataScripts from './DeferredDataScripts.node';
2121
import {
22+
type RouterExtendsHooks,
2223
modifyRoutes as modifyRoutesHook,
2324
onBeforeCreateRoutes as onBeforeCreateRoutesHook,
2425
} from './hooks';
@@ -40,10 +41,7 @@ function createRemixReuqest(request: Request) {
4041
export const routerPlugin = (
4142
userConfig: Partial<RouterConfig> = {},
4243
): RuntimePluginFuture<{
43-
extendHooks: {
44-
modifyRoutes: typeof modifyRoutesHook;
45-
onBeforeCreateRoutes: typeof onBeforeCreateRoutesHook;
46-
};
44+
extendHooks: RouterExtendsHooks;
4745
}> => {
4846
return {
4947
name: '@modern-js/plugin-router',

packages/runtime/plugin-runtime/src/router/runtime/plugin.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useContext, useMemo } from 'react';
1616
import { type RuntimePluginFuture, RuntimeReactContext } from '../../core';
1717
import { getGlobalLayoutApp, getGlobalRoutes } from '../../core/context';
1818
import {
19+
type RouterExtendsHooks,
1920
modifyRoutes as modifyRoutesHook,
2021
onBeforeCreateRoutes as onBeforeCreateRoutesHook,
2122
} from './hooks';
@@ -43,10 +44,7 @@ export function modifyRoutes(modifyFunction: (routes: Routes) => Routes) {
4344
export const routerPlugin = (
4445
userConfig: Partial<RouterConfig> = {},
4546
): RuntimePluginFuture<{
46-
extendHooks: {
47-
modifyRoutes: typeof modifyRoutesHook;
48-
onBeforeCreateRoutes: typeof onBeforeCreateRoutesHook;
49-
};
47+
extendHooks: RouterExtendsHooks;
5048
}> => {
5149
return {
5250
name: '@modern-js/plugin-router',

packages/toolkit/plugin-v2/src/cli/api.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { merge } from '@modern-js/utils/lodash';
2-
import type { PluginHook, PluginHookTap } from '../types';
3-
import type { CLIPluginAPI } from '../types/cli/api';
2+
import type { PluginHook } from '../types';
3+
import type {
4+
AllKeysForCLIPluginExtendsAPI,
5+
AllValueForCLIPluginExtendsAPI,
6+
CLIPluginAPI,
7+
CLIPluginExtendsAPI,
8+
} from '../types/cli/api';
49
import type { AppContext, InternalContext } from '../types/cli/context';
510
import type { CLIPluginExtends } from '../types/cli/plugin';
611
import type { PluginManager } from '../types/plugin';
@@ -48,26 +53,24 @@ export function initPluginAPI<Extends extends CLIPluginExtends>({
4853
return context.hooks;
4954
}
5055

51-
const extendsPluginApi: Record<
52-
string,
53-
PluginHookTap<(...args: any[]) => any>
54-
> = {};
56+
const extendsPluginApi: Partial<CLIPluginExtendsAPI<Extends>> = {};
5557

5658
plugins.forEach(plugin => {
5759
const { _registryApi } = plugin;
5860
if (_registryApi) {
5961
const apis = _registryApi(getAppContext, updateAppContext);
6062
Object.keys(apis).forEach(apiName => {
61-
extendsPluginApi[apiName] = apis[apiName];
63+
extendsPluginApi[apiName as AllKeysForCLIPluginExtendsAPI<Extends>] =
64+
apis[apiName] as AllValueForCLIPluginExtendsAPI<Extends>;
6265
});
6366
}
6467
});
6568

6669
if (extendsHooks) {
6770
Object.keys(extendsHooks!).forEach(hookName => {
68-
extendsPluginApi[hookName] = (
71+
extendsPluginApi[hookName as AllKeysForCLIPluginExtendsAPI<Extends>] = (
6972
extendsHooks as Record<string, PluginHook<(...args: any[]) => any>>
70-
)[hookName].tap;
73+
)[hookName].tap as AllValueForCLIPluginExtendsAPI<Extends>;
7174
});
7275
}
7376

packages/toolkit/plugin-v2/src/runtime/api.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { merge } from '@modern-js/runtime-utils/merge';
2-
import type { PluginHook, PluginHookTap } from '../types';
2+
import type { PluginHook } from '../types';
33
import type { PluginManager } from '../types/plugin';
4-
import type { RuntimePluginAPI } from '../types/runtime/api';
4+
import type {
5+
AllKeysForRuntimePluginExtendsAPI,
6+
AllValueForRuntimePluginExtendsAPI,
7+
RuntimePluginAPI,
8+
RuntimePluginExtendsAPI,
9+
} from '../types/runtime/api';
510
import type {
611
InternalRuntimeContext,
712
RuntimeContext,
@@ -52,30 +57,31 @@ export function initPluginAPI<Extends extends RuntimePluginExtends>({
5257
throw new Error('Cannot access config');
5358
}
5459

55-
const extendsPluginApi: Record<
56-
string,
57-
PluginHookTap<(...args: any[]) => any>
58-
> = {};
60+
const extendsPluginApi: Partial<RuntimePluginExtendsAPI<Extends>> = {};
5961

6062
plugins.forEach(plugin => {
6163
const { _registryApi } = plugin;
6264
if (_registryApi) {
6365
const apis = _registryApi(getRuntimeContext, updateRuntimeContext);
6466
Object.keys(apis).forEach(apiName => {
65-
extendsPluginApi[apiName] = apis[apiName];
67+
extendsPluginApi[apiName as keyof RuntimePluginExtendsAPI<Extends>] =
68+
apis[
69+
apiName
70+
] as RuntimePluginExtendsAPI<Extends>[keyof RuntimePluginExtendsAPI<Extends>];
6671
});
6772
}
6873
});
6974

7075
if (extendsHooks) {
7176
Object.keys(extendsHooks!).forEach(hookName => {
72-
extendsPluginApi[hookName] = (
73-
extendsHooks as Record<string, PluginHook<(...args: any[]) => any>>
74-
)[hookName].tap;
77+
extendsPluginApi[hookName as AllKeysForRuntimePluginExtendsAPI<Extends>] =
78+
(extendsHooks as Record<string, PluginHook<(...args: any[]) => any>>)[
79+
hookName
80+
].tap as AllValueForRuntimePluginExtendsAPI<Extends>;
7581
});
7682
}
7783

78-
return {
84+
const pluginAPI = {
7985
updateRuntimeContext,
8086
getHooks,
8187
getRuntimeConfig,
@@ -85,4 +91,19 @@ export function initPluginAPI<Extends extends RuntimePluginExtends>({
8591
pickContext: hooks.pickContext.tap,
8692
...extendsPluginApi,
8793
};
94+
95+
return new Proxy(pluginAPI, {
96+
get(target: Record<string, any>, prop: string) {
97+
// hack then function to fix p-defer handle error
98+
if (prop === 'then') {
99+
return undefined;
100+
}
101+
if (prop in target) {
102+
return target[prop];
103+
}
104+
return () => {
105+
console.warn(`api.${prop.toString()} not exist`);
106+
};
107+
},
108+
}) as RuntimePluginAPI<Extends>;
88109
}

packages/toolkit/plugin-v2/src/types/cli/api.ts

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
OnDevCompileDoneFn,
77
} from '@rsbuild/core';
88
import type { Hooks } from '../../cli/hooks';
9-
import type { PluginHookTap } from '../hooks';
9+
import type { PluginHook, PluginHookTap } from '../hooks';
1010
import type { DeepPartial } from '../utils';
1111
import type { AppContext } from './context';
1212
import type {
@@ -35,66 +35,86 @@ import type { CLIPluginExtends } from './plugin';
3535
/**
3636
* Define a generic CLI plugin API that provider can extend as needed.
3737
*/
38-
export type CLIPluginAPI<Extends extends CLIPluginExtends> = Readonly<{
39-
isPluginExists: (name: string) => boolean;
40-
getAppContext: () => Readonly<AppContext<Extends> & Extends['extendContext']>;
41-
getConfig: () => Readonly<Extends['config']>;
42-
getNormalizedConfig: () => Readonly<Extends['normalizedConfig']>;
43-
getHooks: () => Readonly<
44-
Hooks<
45-
Extends['config'],
46-
Extends['normalizedConfig'],
47-
Extends['extendBuildUtils']
48-
> &
49-
Extends['extendHooks']
50-
>;
38+
export type CLIPluginAPI<Extends extends CLIPluginExtends> = Readonly<
39+
{
40+
isPluginExists: (name: string) => boolean;
41+
getAppContext: () => Readonly<
42+
AppContext<Extends> & Extends['extendContext']
43+
>;
44+
getConfig: () => Readonly<Extends['config']>;
45+
getNormalizedConfig: () => Readonly<Extends['normalizedConfig']>;
46+
getHooks: () => Readonly<
47+
Hooks<
48+
Extends['config'],
49+
Extends['normalizedConfig'],
50+
Extends['extendBuildUtils']
51+
> &
52+
Extends['extendHooks']
53+
>;
5154

52-
updateAppContext: (
53-
appContext: DeepPartial<AppContext<Extends> & Extends['extendContext']>,
54-
) => void;
55+
updateAppContext: (
56+
appContext: DeepPartial<AppContext<Extends> & Extends['extendContext']>,
57+
) => void;
5558

56-
// config hooks
57-
config: PluginHookTap<ConfigFn<DeepPartial<Extends['config']>>>;
58-
modifyConfig: PluginHookTap<ModifyConfigFn<Extends['config']>>;
59-
modifyResolvedConfig: PluginHookTap<
60-
ModifyResolvedConfigFn<Extends['normalizedConfig']>
61-
>;
59+
// config hooks
60+
config: PluginHookTap<ConfigFn<DeepPartial<Extends['config']>>>;
61+
modifyConfig: PluginHookTap<ModifyConfigFn<Extends['config']>>;
62+
modifyResolvedConfig: PluginHookTap<
63+
ModifyResolvedConfigFn<Extends['normalizedConfig']>
64+
>;
6265

63-
// modify rsbuild config hooks
64-
modifyRsbuildConfig: PluginHookTap<
65-
ModifyRsbuildConfigFn<Extends['extendBuildUtils']>
66-
>;
67-
modifyBundlerChain: PluginHookTap<
68-
ModifyBundlerChainFn<Extends['extendBuildUtils']>
69-
>;
70-
/** Only works when bundler is Rspack */
71-
modifyRspackConfig: PluginHookTap<
72-
ModifyRspackConfigFn<Extends['extendBuildUtils']>
73-
>;
74-
/** Only works when bundler is Webpack */
75-
modifyWebpackChain: PluginHookTap<
76-
ModifyWebpackChainFn<Extends['extendBuildUtils']>
77-
>;
78-
/** Only works when bundler is Webpack */
79-
modifyWebpackConfig: PluginHookTap<
80-
ModifyWebpackConfigFn<Extends['extendBuildUtils']>
66+
// modify rsbuild config hooks
67+
modifyRsbuildConfig: PluginHookTap<
68+
ModifyRsbuildConfigFn<Extends['extendBuildUtils']>
69+
>;
70+
modifyBundlerChain: PluginHookTap<
71+
ModifyBundlerChainFn<Extends['extendBuildUtils']>
72+
>;
73+
/** Only works when bundler is Rspack */
74+
modifyRspackConfig: PluginHookTap<
75+
ModifyRspackConfigFn<Extends['extendBuildUtils']>
76+
>;
77+
/** Only works when bundler is Webpack */
78+
modifyWebpackChain: PluginHookTap<
79+
ModifyWebpackChainFn<Extends['extendBuildUtils']>
80+
>;
81+
/** Only works when bundler is Webpack */
82+
modifyWebpackConfig: PluginHookTap<
83+
ModifyWebpackConfigFn<Extends['extendBuildUtils']>
84+
>;
85+
modifyHtmlPartials: PluginHookTap<ModifyHtmlPartialsFn>;
86+
87+
addCommand: PluginHookTap<AddCommandFn>;
88+
89+
onPrepare: PluginHookTap<OnPrepareFn>;
90+
addWatchFiles: PluginHookTap<AddWatchFilesFn>;
91+
onFileChanged: PluginHookTap<OnFileChangedFn>;
92+
onBeforeRestart: PluginHookTap<OnBeforeRestartFn>;
93+
onBeforeCreateCompiler: PluginHookTap<OnBeforeCreateCompilerFn>;
94+
onAfterCreateCompiler: PluginHookTap<OnAfterCreateCompilerFn>;
95+
onDevCompileDone: PluginHookTap<OnDevCompileDoneFn>;
96+
onBeforeBuild: PluginHookTap<OnBeforeBuildFn>;
97+
onAfterBuild: PluginHookTap<OnAfterBuildFn>;
98+
onBeforeDev: PluginHookTap<OnBeforeDevFn>;
99+
onAfterDev: PluginHookTap<OnAfterDevFn>;
100+
onBeforeDeploy: PluginHookTap<OnBeforeDeployFn>;
101+
onAfterDeploy: PluginHookTap<OnAfterDeployFn>;
102+
onBeforeExit: PluginHookTap<OnBeforeExitFn>;
103+
} & CLIPluginExtendsAPI<Extends>
104+
>;
105+
106+
export type CLIPluginExtendsAPI<Extends extends CLIPluginExtends> = {
107+
[K in keyof Extends['extendHooks']]: PluginHookTap<
108+
Extends['extendHooks'][K] extends PluginHook<infer Args>
109+
? Args extends (...args: any[]) => any
110+
? Args
111+
: (...args: any[]) => any
112+
: (...args: any[]) => any
81113
>;
82-
modifyHtmlPartials: PluginHookTap<ModifyHtmlPartialsFn>;
114+
} & Extends['extendApi'];
83115

84-
addCommand: PluginHookTap<AddCommandFn>;
116+
export type AllKeysForCLIPluginExtendsAPI<Extends extends CLIPluginExtends> =
117+
keyof CLIPluginExtendsAPI<Extends>;
85118

86-
onPrepare: PluginHookTap<OnPrepareFn>;
87-
addWatchFiles: PluginHookTap<AddWatchFilesFn>;
88-
onFileChanged: PluginHookTap<OnFileChangedFn>;
89-
onBeforeRestart: PluginHookTap<OnBeforeRestartFn>;
90-
onBeforeCreateCompiler: PluginHookTap<OnBeforeCreateCompilerFn>;
91-
onAfterCreateCompiler: PluginHookTap<OnAfterCreateCompilerFn>;
92-
onDevCompileDone: PluginHookTap<OnDevCompileDoneFn>;
93-
onBeforeBuild: PluginHookTap<OnBeforeBuildFn>;
94-
onAfterBuild: PluginHookTap<OnAfterBuildFn>;
95-
onBeforeDev: PluginHookTap<OnBeforeDevFn>;
96-
onAfterDev: PluginHookTap<OnAfterDevFn>;
97-
onBeforeDeploy: PluginHookTap<OnBeforeDeployFn>;
98-
onAfterDeploy: PluginHookTap<OnAfterDeployFn>;
99-
onBeforeExit: PluginHookTap<OnBeforeExitFn>;
100-
}>;
119+
export type AllValueForCLIPluginExtendsAPI<Extends extends CLIPluginExtends> =
120+
CLIPluginExtendsAPI<Extends>[AllKeysForCLIPluginExtendsAPI<Extends>];

0 commit comments

Comments
 (0)