@module-federation/bridge-vue3 2.3.1 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "git+https://github.com/module-federation/core.git",
8
8
  "directory": "packages/bridge/vue3-bridge"
9
9
  },
10
- "version": "2.3.1",
10
+ "version": "2.3.2",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -31,9 +31,9 @@
31
31
  "vue-router": "=4"
32
32
  },
33
33
  "dependencies": {
34
- "@module-federation/bridge-shared": "2.3.1",
35
- "@module-federation/runtime": "2.3.1",
36
- "@module-federation/sdk": "2.3.1"
34
+ "@module-federation/bridge-shared": "2.3.2",
35
+ "@module-federation/sdk": "2.3.2",
36
+ "@module-federation/runtime": "2.3.2"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "^18.3.11",
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { createBridgeComponent } from './provider';
2
+ export type { ProviderFnParams } from './provider';
2
3
  export { createRemoteComponent, createRemoteAppComponent } from './create';
3
4
  export type { RenderFnParams } from '@module-federation/bridge-shared';
package/src/provider.ts CHANGED
@@ -16,9 +16,13 @@ type AddOptionsFnParams = {
16
16
 
17
17
  export type ProviderFnParams = {
18
18
  rootComponent: Vue.Component;
19
- appOptions: (
20
- params: AddOptionsFnParams,
21
- ) => { router?: VueRouter.Router } | void;
19
+ appOptions: (params: AddOptionsFnParams) => {
20
+ router?: VueRouter.Router;
21
+ /** Called with the bridge's internal router after creation but before navigation.
22
+ * Use this to register global guards (beforeEach, afterEach, etc.) that would
23
+ * otherwise be lost when the bridge recreates the router. */
24
+ afterRouterCreate?: (router: VueRouter.Router) => void;
25
+ } | void;
22
26
  };
23
27
 
24
28
  export function createBridgeComponent(bridgeInfo: ProviderFnParams) {
@@ -59,7 +63,7 @@ export function createBridgeComponent(bridgeInfo: ProviderFnParams) {
59
63
  ...extraProps,
60
64
  });
61
65
  if (bridgeOptions?.router) {
62
- const { history, routes } = processRoutes({
66
+ const { history, routes, patchRouter } = processRoutes({
63
67
  router: bridgeOptions.router,
64
68
  basename: info.basename,
65
69
  memoryRoute: info.memoryRoute,
@@ -72,6 +76,14 @@ export function createBridgeComponent(bridgeInfo: ProviderFnParams) {
72
76
  routes,
73
77
  });
74
78
 
79
+ if (patchRouter) {
80
+ patchRouter(router);
81
+ }
82
+
83
+ if (bridgeOptions.afterRouterCreate) {
84
+ bridgeOptions.afterRouterCreate(router);
85
+ }
86
+
75
87
  LoggerInstance.debug(`createBridgeComponent render router info>>>`, {
76
88
  moduleName,
77
89
  router,
package/src/routeUtils.ts CHANGED
@@ -10,6 +10,7 @@ export interface RouteProcessingOptions {
10
10
  export interface RouteProcessingResult {
11
11
  history: VueRouter.RouterHistory;
12
12
  routes: VueRouter.RouteRecordNormalized[];
13
+ patchRouter?: (router: VueRouter.Router) => void;
13
14
  }
14
15
 
15
16
  /**
@@ -68,6 +69,59 @@ function addBasenameToNestedRoutes(
68
69
  });
69
70
  }
70
71
 
72
+ /**
73
+ * Create a patch function that rewrites path-based navigations to include
74
+ * the basename prefix. This is needed because createWebHashHistory() does
75
+ * not accept a basename argument, so router.push('/foo') would bypass the
76
+ * prefixed route definitions.
77
+ *
78
+ * By patching push/replace/resolve *before* Vue Router resolves the
79
+ * location we also avoid the "No match found" console warning.
80
+ */
81
+ function createHashBasenamePatch(
82
+ basename: string,
83
+ ): (router: VueRouter.Router) => void {
84
+ const normalized = basename.replace(/\/+$/, '');
85
+
86
+ /**
87
+ * Only absolute paths (starting with '/') that don't already carry the
88
+ * basename prefix need rewriting. Relative segments ('settings'),
89
+ * query-only ('?tab=1'), and hash-only ('#anchor') strings are resolved
90
+ * by Vue Router against the current route and must pass through untouched.
91
+ */
92
+ const needsPrefix = (path: string): boolean =>
93
+ path.startsWith('/') &&
94
+ path !== normalized &&
95
+ !path.startsWith(normalized + '/');
96
+
97
+ const prefix = (path: string): string =>
98
+ `${normalized}${path}`.replace(/\/+/g, '/');
99
+
100
+ const rewrite = (
101
+ to: VueRouter.RouteLocationRaw,
102
+ ): VueRouter.RouteLocationRaw => {
103
+ if (typeof to === 'string') {
104
+ return needsPrefix(to) ? prefix(to) : to;
105
+ }
106
+ if ('path' in to && typeof to.path === 'string' && needsPrefix(to.path)) {
107
+ return { ...to, path: prefix(to.path) };
108
+ }
109
+ return to;
110
+ };
111
+
112
+ return (router) => {
113
+ const originalPush = router.push.bind(router);
114
+ const originalReplace = router.replace.bind(router);
115
+ const originalResolve = router.resolve.bind(router);
116
+
117
+ router.push = (to: VueRouter.RouteLocationRaw) => originalPush(rewrite(to));
118
+ router.replace = (to: VueRouter.RouteLocationRaw) =>
119
+ originalReplace(rewrite(to));
120
+ router.resolve = ((to: VueRouter.RouteLocationRaw, ...rest: any[]) =>
121
+ originalResolve(rewrite(to), ...rest)) as typeof router.resolve;
122
+ };
123
+ }
124
+
71
125
  /**
72
126
  * Route processing solution based on path analysis
73
127
  *
@@ -146,21 +200,19 @@ export function processRoutes(
146
200
  }
147
201
 
148
202
  let history: VueRouter.RouterHistory;
203
+ let patchRouter: ((router: VueRouter.Router) => void) | undefined;
204
+
149
205
  if (memoryRoute) {
150
- // Memory route mode
151
206
  history = VueRouter.createMemoryHistory(basename);
152
207
  } else if (hashRoute) {
153
- // Hash route mode
154
208
  history = VueRouter.createWebHashHistory();
155
- // Recursively process nested routes and add basename prefix to all paths
156
- if (basename) routes = addBasenameToNestedRoutes(routes, basename);
209
+ if (basename) {
210
+ routes = addBasenameToNestedRoutes(routes, basename);
211
+ patchRouter = createHashBasenamePatch(basename);
212
+ }
157
213
  } else {
158
- // Default Web History mode
159
214
  history = VueRouter.createWebHistory(basename);
160
215
  }
161
216
 
162
- return {
163
- history,
164
- routes,
165
- };
217
+ return { history, routes, patchRouter };
166
218
  }