@module-federation/nextjs-mf 5.2.1 → 5.3.0

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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/lib/NextFederationPlugin.js +1 -481
  3. package/lib/NextFederationPlugin2.js +536 -0
  4. package/lib/_virtual/UrlNode.js +8 -0
  5. package/lib/_virtual/_commonjsHelpers.js +26 -0
  6. package/lib/_virtual/_tslib.js +101 -0
  7. package/lib/_virtual/helpers.js +7 -0
  8. package/lib/_virtual/nextPageMapLoader.js +7 -0
  9. package/lib/client/CombinedPages.d.ts +28 -0
  10. package/lib/client/CombinedPages.d.ts.map +1 -0
  11. package/lib/client/CombinedPages.js +60 -0
  12. package/lib/client/MFClient.d.ts +70 -0
  13. package/lib/client/MFClient.d.ts.map +1 -0
  14. package/lib/client/MFClient.js +197 -0
  15. package/lib/client/RemoteContainer.d.ts +58 -0
  16. package/lib/client/RemoteContainer.d.ts.map +1 -0
  17. package/lib/client/RemoteContainer.js +161 -0
  18. package/lib/client/RemotePages.d.ts +48 -0
  19. package/lib/client/RemotePages.d.ts.map +1 -0
  20. package/lib/client/RemotePages.js +168 -0
  21. package/lib/client/UrlNode.d.ts +18 -0
  22. package/lib/client/UrlNode.d.ts.map +1 -0
  23. package/lib/client/UrlNode.js +162 -0
  24. package/lib/client/helpers.d.ts +17 -0
  25. package/lib/client/helpers.d.ts.map +1 -0
  26. package/lib/client/helpers.js +108 -0
  27. package/lib/client/useMFClient.d.ts +25 -0
  28. package/lib/client/useMFClient.d.ts.map +1 -0
  29. package/lib/client/useMFClient.js +79 -0
  30. package/lib/client/useMFRemote.d.ts +17 -0
  31. package/lib/client/useMFRemote.d.ts.map +1 -0
  32. package/lib/client/useMFRemote.js +72 -0
  33. package/lib/loaders/UrlNode.js +215 -0
  34. package/lib/loaders/fixImageLoader.js +42 -8
  35. package/lib/loaders/helpers.js +21 -2
  36. package/lib/loaders/nextPageMapLoader.js +81 -17
  37. package/lib/loaders/patchNextClientPageLoader.js +53 -0
  38. package/lib/plugins/DevHmrFixInvalidPongPlugin.js +65 -0
  39. package/lib/utils.js +7 -3
  40. package/package.json +21 -4
  41. package/tsconfig.json +33 -0
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _tslib = require('../_virtual/_tslib.js');
6
+
7
+ /**
8
+ * A class which prepares/loads a list of remotes pages and knows how
9
+ * to prepare NextJS pseudo-module of route data.
10
+ */
11
+ var RemotePages = /** @class */ (function () {
12
+ function RemotePages(remoteToRoutes) {
13
+ var _this = this;
14
+ this.paths = {};
15
+ this.asyncLoadedPageMaps = new Set();
16
+ this.pageListCache = undefined;
17
+ remoteToRoutes === null || remoteToRoutes === void 0 ? void 0 : remoteToRoutes.forEach(function (routes, remote) {
18
+ _this.addRoutes(routes, remote);
19
+ });
20
+ }
21
+ /**
22
+ * Add remote routes for specific RemoteContainer which serves them.
23
+ */
24
+ RemotePages.prototype.addRoutes = function (routes, remote) {
25
+ var _this = this;
26
+ if (Array.isArray(routes)) {
27
+ routes.forEach(function (route) {
28
+ _this.paths[route] = remote;
29
+ });
30
+ }
31
+ else {
32
+ this.paths[routes] = remote;
33
+ }
34
+ this.pageListCache = Object.keys(this.paths);
35
+ };
36
+ /**
37
+ * Load a remote page map and add its routes to registry.
38
+ */
39
+ RemotePages.prototype.loadRemotePageMap = function (remote) {
40
+ return _tslib.__awaiter(this, void 0, void 0, function () {
41
+ var pageMap;
42
+ return _tslib.__generator(this, function (_a) {
43
+ switch (_a.label) {
44
+ case 0: return [4 /*yield*/, remote.getPageMap()];
45
+ case 1:
46
+ pageMap = _a.sent();
47
+ if (!pageMap)
48
+ return [2 /*return*/, undefined];
49
+ // init once page map from remote if it wasn't done before
50
+ // here we updating real routes received from remote app in runtime
51
+ if (!this.asyncLoadedPageMaps.has(remote)) {
52
+ this.asyncLoadedPageMaps.add(remote);
53
+ this.addRoutes(Object.keys(pageMap), remote);
54
+ }
55
+ return [2 /*return*/, pageMap];
56
+ }
57
+ });
58
+ });
59
+ };
60
+ /**
61
+ * Get remote module according to provided next route.
62
+ * Under the hood it automatically determines which remote need to be used.
63
+ */
64
+ RemotePages.prototype.routeToPageModule = function (route) {
65
+ return _tslib.__awaiter(this, void 0, void 0, function () {
66
+ var remote, pageMap, modulePath;
67
+ return _tslib.__generator(this, function (_a) {
68
+ switch (_a.label) {
69
+ case 0:
70
+ remote = this.routeToRemote(route);
71
+ if (!remote) {
72
+ return [2 /*return*/, undefined];
73
+ }
74
+ return [4 /*yield*/, this.loadRemotePageMap(remote)];
75
+ case 1:
76
+ pageMap = _a.sent();
77
+ if (!pageMap) {
78
+ return [2 /*return*/, undefined];
79
+ }
80
+ modulePath = pageMap[route];
81
+ if (!modulePath) {
82
+ return [2 /*return*/, undefined];
83
+ }
84
+ return [2 /*return*/, remote.getModule(modulePath)];
85
+ }
86
+ });
87
+ });
88
+ };
89
+ /**
90
+ * Check that provided route present in registry.
91
+ */
92
+ RemotePages.prototype.hasRoute = function (route) {
93
+ var _a;
94
+ return !!((_a = this.pageListCache) === null || _a === void 0 ? void 0 : _a.includes(route));
95
+ };
96
+ /**
97
+ * Find remote according to provided route.
98
+ */
99
+ RemotePages.prototype.routeToRemote = function (route) {
100
+ var bestMatch;
101
+ for (var basepath in this.paths) {
102
+ if (route === basepath) {
103
+ return this.paths[basepath];
104
+ }
105
+ else if (route.startsWith("".concat(basepath, "/"))) {
106
+ if (!bestMatch) {
107
+ bestMatch = basepath;
108
+ }
109
+ else if (bestMatch.length < basepath.length) {
110
+ bestMatch = basepath;
111
+ }
112
+ }
113
+ }
114
+ if (bestMatch) {
115
+ return this.paths[bestMatch];
116
+ }
117
+ return undefined;
118
+ };
119
+ /**
120
+ * Get cached unsorted list of remote routes.
121
+ */
122
+ RemotePages.prototype.getPageList = function () {
123
+ // it's very important to return the same Array instance
124
+ // because it instance is used in CombinedPages for recalculation of sorted version of all routes
125
+ var pageList = this.pageListCache || Object.keys(this.paths);
126
+ this.pageListCache = pageList;
127
+ return pageList;
128
+ };
129
+ /**
130
+ * Get prepared pseudo-module which is consumed by Nextjs' router.
131
+ */
132
+ RemotePages.prototype.getRouteInfo = function (route) {
133
+ return _tslib.__awaiter(this, void 0, void 0, function () {
134
+ var routeInfo, mod, e_1;
135
+ return _tslib.__generator(this, function (_a) {
136
+ switch (_a.label) {
137
+ case 0:
138
+ _a.trys.push([0, 2, , 3]);
139
+ return [4 /*yield*/, this.routeToPageModule(route)];
140
+ case 1:
141
+ mod = _a.sent();
142
+ if (mod) {
143
+ routeInfo = {
144
+ component: mod.default,
145
+ exports: mod,
146
+ styles: [],
147
+ };
148
+ }
149
+ return [3 /*break*/, 3];
150
+ case 2:
151
+ e_1 = _a.sent();
152
+ routeInfo = {
153
+ // TODO: provide ability to customize component with Error
154
+ component: function () { return e_1.message; },
155
+ exports: {},
156
+ styles: [],
157
+ };
158
+ console.warn(e_1);
159
+ return [3 /*break*/, 3];
160
+ case 3: return [2 /*return*/, routeInfo];
161
+ }
162
+ });
163
+ });
164
+ };
165
+ return RemotePages;
166
+ }());
167
+
168
+ exports.RemotePages = RemotePages;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This class provides a logic of sorting dynamic routes in NextJS.
3
+ *
4
+ * It was copied from
5
+ * @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
6
+ */
7
+ export declare class UrlNode {
8
+ placeholder: boolean;
9
+ children: Map<string, UrlNode>;
10
+ slugName: string | null;
11
+ restSlugName: string | null;
12
+ optionalRestSlugName: string | null;
13
+ insert(urlPath: string): void;
14
+ smoosh(): string[];
15
+ private _smoosh;
16
+ private _insert;
17
+ }
18
+ //# sourceMappingURL=UrlNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UrlNode.d.ts","sourceRoot":"","sources":["../../src/client/UrlNode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,WAAW,EAAE,OAAO,CAAQ;IAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAa;IAC3C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI7B,MAAM,IAAI,MAAM,EAAE;IAIlB,OAAO,CAAC,OAAO;IAoDf,OAAO,CAAC,OAAO;CAmIhB"}
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _tslib = require('../_virtual/_tslib.js');
6
+
7
+ /**
8
+ * This class provides a logic of sorting dynamic routes in NextJS.
9
+ *
10
+ * It was copied from
11
+ * @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
12
+ */
13
+ var UrlNode = /** @class */ (function () {
14
+ function UrlNode() {
15
+ this.placeholder = true;
16
+ this.children = new Map();
17
+ this.slugName = null;
18
+ this.restSlugName = null;
19
+ this.optionalRestSlugName = null;
20
+ }
21
+ UrlNode.prototype.insert = function (urlPath) {
22
+ this._insert(urlPath.split('/').filter(Boolean), [], false);
23
+ };
24
+ UrlNode.prototype.smoosh = function () {
25
+ return this._smoosh();
26
+ };
27
+ UrlNode.prototype._smoosh = function (prefix) {
28
+ var _this = this;
29
+ if (prefix === void 0) { prefix = '/'; }
30
+ var childrenPaths = _tslib.__spreadArray([], _tslib.__read(this.children.keys()), false).sort();
31
+ if (this.slugName !== null) {
32
+ childrenPaths.splice(childrenPaths.indexOf('[]'), 1);
33
+ }
34
+ if (this.restSlugName !== null) {
35
+ childrenPaths.splice(childrenPaths.indexOf('[...]'), 1);
36
+ }
37
+ if (this.optionalRestSlugName !== null) {
38
+ childrenPaths.splice(childrenPaths.indexOf('[[...]]'), 1);
39
+ }
40
+ var routes = childrenPaths
41
+ .map(function (c) { return _this.children.get(c)._smoosh("".concat(prefix).concat(c, "/")); })
42
+ .reduce(function (prev, curr) { return _tslib.__spreadArray(_tslib.__spreadArray([], _tslib.__read(prev), false), _tslib.__read(curr), false); }, []);
43
+ if (this.slugName !== null) {
44
+ routes.push.apply(routes, _tslib.__spreadArray([], _tslib.__read(this.children.get('[]')._smoosh("".concat(prefix, "[").concat(this.slugName, "]/"))), false));
45
+ }
46
+ if (!this.placeholder) {
47
+ var r = prefix === '/' ? '/' : prefix.slice(0, -1);
48
+ if (this.optionalRestSlugName != null) {
49
+ throw new Error("You cannot define a route with the same specificity as a optional catch-all route (\"".concat(r, "\" and \"").concat(r, "[[...").concat(this.optionalRestSlugName, "]]\")."));
50
+ }
51
+ routes.unshift(r);
52
+ }
53
+ if (this.restSlugName !== null) {
54
+ routes.push.apply(routes, _tslib.__spreadArray([], _tslib.__read(this.children
55
+ .get('[...]')
56
+ ._smoosh("".concat(prefix, "[...").concat(this.restSlugName, "]/"))), false));
57
+ }
58
+ if (this.optionalRestSlugName !== null) {
59
+ routes.push.apply(routes, _tslib.__spreadArray([], _tslib.__read(this.children
60
+ .get('[[...]]')
61
+ ._smoosh("".concat(prefix, "[[...").concat(this.optionalRestSlugName, "]]/"))), false));
62
+ }
63
+ return routes;
64
+ };
65
+ UrlNode.prototype._insert = function (urlPaths, slugNames, isCatchAll) {
66
+ if (urlPaths.length === 0) {
67
+ this.placeholder = false;
68
+ return;
69
+ }
70
+ if (isCatchAll) {
71
+ throw new Error("Catch-all must be the last part of the URL.");
72
+ }
73
+ // The next segment in the urlPaths list
74
+ var nextSegment = urlPaths[0];
75
+ // Check if the segment matches `[something]`
76
+ if (nextSegment.startsWith('[') && nextSegment.endsWith(']')) {
77
+ // Strip `[` and `]`, leaving only `something`
78
+ var segmentName = nextSegment.slice(1, -1);
79
+ var isOptional = false;
80
+ if (segmentName.startsWith('[') && segmentName.endsWith(']')) {
81
+ // Strip optional `[` and `]`, leaving only `something`
82
+ segmentName = segmentName.slice(1, -1);
83
+ isOptional = true;
84
+ }
85
+ if (segmentName.startsWith('...')) {
86
+ // Strip `...`, leaving only `something`
87
+ segmentName = segmentName.substring(3);
88
+ isCatchAll = true;
89
+ }
90
+ if (segmentName.startsWith('[') || segmentName.endsWith(']')) {
91
+ throw new Error("Segment names may not start or end with extra brackets ('".concat(segmentName, "')."));
92
+ }
93
+ if (segmentName.startsWith('.')) {
94
+ throw new Error("Segment names may not start with erroneous periods ('".concat(segmentName, "')."));
95
+ }
96
+ var handleSlug = function handleSlug(previousSlug, nextSlug) {
97
+ if (previousSlug !== null) {
98
+ // If the specific segment already has a slug but the slug is not `something`
99
+ // This prevents collisions like:
100
+ // pages/[post]/index.js
101
+ // pages/[id]/index.js
102
+ // Because currently multiple dynamic params on the same segment level are not supported
103
+ if (previousSlug !== nextSlug) {
104
+ // TODO: This error seems to be confusing for users, needs an error link, the description can be based on above comment.
105
+ throw new Error("You cannot use different slug names for the same dynamic path ('".concat(previousSlug, "' !== '").concat(nextSlug, "')."));
106
+ }
107
+ }
108
+ slugNames.forEach(function (slug) {
109
+ if (slug === nextSlug) {
110
+ throw new Error("You cannot have the same slug name \"".concat(nextSlug, "\" repeat within a single dynamic path"));
111
+ }
112
+ if (slug.replace(/\W/g, '') === nextSegment.replace(/\W/g, '')) {
113
+ throw new Error("You cannot have the slug names \"".concat(slug, "\" and \"").concat(nextSlug, "\" differ only by non-word symbols within a single dynamic path"));
114
+ }
115
+ });
116
+ slugNames.push(nextSlug);
117
+ };
118
+ if (isCatchAll) {
119
+ if (isOptional) {
120
+ if (this.restSlugName != null) {
121
+ throw new Error("You cannot use both an required and optional catch-all route at the same level (\"[...".concat(this.restSlugName, "]\" and \"").concat(urlPaths[0], "\" )."));
122
+ }
123
+ handleSlug(this.optionalRestSlugName, segmentName);
124
+ // slugName is kept as it can only be one particular slugName
125
+ this.optionalRestSlugName = segmentName;
126
+ // nextSegment is overwritten to [[...]] so that it can later be sorted specifically
127
+ nextSegment = '[[...]]';
128
+ }
129
+ else {
130
+ if (this.optionalRestSlugName != null) {
131
+ throw new Error("You cannot use both an optional and required catch-all route at the same level (\"[[...".concat(this.optionalRestSlugName, "]]\" and \"").concat(urlPaths[0], "\")."));
132
+ }
133
+ handleSlug(this.restSlugName, segmentName);
134
+ // slugName is kept as it can only be one particular slugName
135
+ this.restSlugName = segmentName;
136
+ // nextSegment is overwritten to [...] so that it can later be sorted specifically
137
+ nextSegment = '[...]';
138
+ }
139
+ }
140
+ else {
141
+ if (isOptional) {
142
+ throw new Error("Optional route parameters are not yet supported (\"".concat(urlPaths[0], "\")."));
143
+ }
144
+ handleSlug(this.slugName, segmentName);
145
+ // slugName is kept as it can only be one particular slugName
146
+ this.slugName = segmentName;
147
+ // nextSegment is overwritten to [] so that it can later be sorted specifically
148
+ nextSegment = '[]';
149
+ }
150
+ }
151
+ // If this UrlNode doesn't have the nextSegment yet we create a new child UrlNode
152
+ if (!this.children.has(nextSegment)) {
153
+ this.children.set(nextSegment, new UrlNode());
154
+ }
155
+ this.children
156
+ .get(nextSegment)
157
+ ._insert(urlPaths.slice(1), slugNames, isCatchAll);
158
+ };
159
+ return UrlNode;
160
+ }());
161
+
162
+ exports.UrlNode = UrlNode;
@@ -0,0 +1,17 @@
1
+ export declare function isDynamicRoute(route: string): boolean;
2
+ export declare function getRouteRegex(normalizedRoute: string): {
3
+ re: RegExp;
4
+ groups: {};
5
+ };
6
+ /**
7
+ * Convert browser pathname to NextJs route.
8
+ * This method is required for proper work of Dynamic routes in NextJS.
9
+ */
10
+ export declare function pathnameToRoute(cleanPathname: string, routes: string[]): string | undefined;
11
+ /**
12
+ * Sort provided pages in correct nextjs order.
13
+ * This sorting is required if you are using dynamic routes in your apps.
14
+ * If order is incorrect then Nextjs may use dynamicRoute instead of exact page.
15
+ */
16
+ export declare function sortNextPages(pages: string[]): string[];
17
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/client/helpers.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,WAE3C;AA4CD,wBAAgB,aAAa,CAAC,eAAe,EAAE,MAAM;;;EAMpD;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EAAE,GACf,MAAM,GAAG,SAAS,CAYpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKvD"}
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _tslib = require('../_virtual/_tslib.js');
6
+ var UrlNode = require('./UrlNode.js');
7
+
8
+ var TEST_DYNAMIC_ROUTE = /\/\[[^/]+?\](?=\/|$)/;
9
+ function isDynamicRoute(route) {
10
+ return TEST_DYNAMIC_ROUTE.test(route);
11
+ }
12
+ /**
13
+ * Parses a given parameter from a route to a data structure that can be used
14
+ * to generate the parametrized route. Examples:
15
+ * - `[...slug]` -> `{ name: 'slug', repeat: true, optional: true }`
16
+ * - `[foo]` -> `{ name: 'foo', repeat: false, optional: true }`
17
+ * - `bar` -> `{ name: 'bar', repeat: false, optional: false }`
18
+ */
19
+ function parseParameter(param) {
20
+ var optional = param.startsWith('[') && param.endsWith(']');
21
+ if (optional) {
22
+ param = param.slice(1, -1);
23
+ }
24
+ var repeat = param.startsWith('...');
25
+ if (repeat) {
26
+ param = param.slice(3);
27
+ }
28
+ return { key: param, repeat: repeat, optional: optional };
29
+ }
30
+ function getParametrizedRoute(route) {
31
+ // const segments = removeTrailingSlash(route).slice(1).split('/')
32
+ var segments = route.slice(1).split('/');
33
+ var groups = {};
34
+ var groupIndex = 1;
35
+ return {
36
+ parameterizedRoute: segments
37
+ .map(function (segment) {
38
+ if (segment.startsWith('[') && segment.endsWith(']')) {
39
+ var _a = parseParameter(segment.slice(1, -1)), key = _a.key, optional = _a.optional, repeat = _a.repeat;
40
+ groups[key] = { pos: groupIndex++, repeat: repeat, optional: optional };
41
+ return repeat ? (optional ? '(?:/(.+?))?' : '/(.+?)') : '/([^/]+?)';
42
+ }
43
+ else {
44
+ return "/".concat(escapeStringRegexp(segment));
45
+ }
46
+ })
47
+ .join(''),
48
+ groups: groups,
49
+ };
50
+ }
51
+ function getRouteRegex(normalizedRoute) {
52
+ var _a = getParametrizedRoute(normalizedRoute), parameterizedRoute = _a.parameterizedRoute, groups = _a.groups;
53
+ return {
54
+ re: new RegExp("^".concat(parameterizedRoute, "(?:/)?$")),
55
+ groups: groups,
56
+ };
57
+ }
58
+ var reHasRegExp = /[|\\{}()[\]^$+*?.-]/;
59
+ var reReplaceRegExp = /[|\\{}()[\]^$+*?.-]/g;
60
+ function escapeStringRegexp(str) {
61
+ // see also: https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/escapeRegExp.js#L23
62
+ if (reHasRegExp.test(str)) {
63
+ return str.replace(reReplaceRegExp, '\\$&');
64
+ }
65
+ return str;
66
+ }
67
+ /**
68
+ * Convert browser pathname to NextJs route.
69
+ * This method is required for proper work of Dynamic routes in NextJS.
70
+ */
71
+ function pathnameToRoute(cleanPathname, routes) {
72
+ var e_1, _a;
73
+ if (routes.includes(cleanPathname)) {
74
+ return cleanPathname;
75
+ }
76
+ try {
77
+ for (var routes_1 = _tslib.__values(routes), routes_1_1 = routes_1.next(); !routes_1_1.done; routes_1_1 = routes_1.next()) {
78
+ var route = routes_1_1.value;
79
+ if (isDynamicRoute(route) && getRouteRegex(route).re.test(cleanPathname)) {
80
+ return route;
81
+ }
82
+ }
83
+ }
84
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
85
+ finally {
86
+ try {
87
+ if (routes_1_1 && !routes_1_1.done && (_a = routes_1.return)) _a.call(routes_1);
88
+ }
89
+ finally { if (e_1) throw e_1.error; }
90
+ }
91
+ return undefined;
92
+ }
93
+ /**
94
+ * Sort provided pages in correct nextjs order.
95
+ * This sorting is required if you are using dynamic routes in your apps.
96
+ * If order is incorrect then Nextjs may use dynamicRoute instead of exact page.
97
+ */
98
+ function sortNextPages(pages) {
99
+ var root = new UrlNode.UrlNode();
100
+ pages.forEach(function (pageRoute) { return root.insert(pageRoute); });
101
+ // Smoosh will then sort those sublevels up to the point where you get the correct route definition priority
102
+ return root.smoosh();
103
+ }
104
+
105
+ exports.getRouteRegex = getRouteRegex;
106
+ exports.isDynamicRoute = isDynamicRoute;
107
+ exports.pathnameToRoute = pathnameToRoute;
108
+ exports.sortNextPages = sortNextPages;
@@ -0,0 +1,25 @@
1
+ import type { MFClient } from './MFClient';
2
+ import type { RemoteContainer } from './RemoteContainer';
3
+ export declare type MFClientHookOptions = {
4
+ /**
5
+ * This callback will be called when user switches to federated page
6
+ * - as a first arg you will receive RemoteContainer
7
+ * If user return back to the host application page
8
+ * - then the first argument became `undefined`
9
+ *
10
+ * This callback is called only if changed remote from which served current visible page
11
+ * and does not called on internal nextjs route changes.
12
+ *
13
+ * This callback helps in very convenient way in _app.tsx (or any other React component)
14
+ * load additional data from RemoteContainer and pass it to your application. Eg.:
15
+ * - application menu
16
+ * - apollo configs
17
+ * - translation strings
18
+ */
19
+ onChangeRemote?: (remote: RemoteContainer | undefined, MFClient: MFClient) => void;
20
+ };
21
+ /**
22
+ * React hook which provides convenient way for working with ModuleFederation runtime changes in runtime;
23
+ */
24
+ export declare function useMFClient(opts: MFClientHookOptions): MFClient;
25
+ //# sourceMappingURL=useMFClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMFClient.d.ts","sourceRoot":"","sources":["../../src/client/useMFClient.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,oBAAY,mBAAmB,GAAG;IAChC;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,QAAQ,EAAE,QAAQ,KACf,IAAI,CAAC;CACX,CAAC;AAQF;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG,QAAQ,CAgD/D"}
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var singletonRouter = require('next/dist/client/router');
7
+
8
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n["default"] = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
+ var singletonRouter__default = /*#__PURE__*/_interopDefaultLegacy(singletonRouter);
30
+
31
+ var isBrowser = typeof window !== 'undefined';
32
+ /**
33
+ * React hook which provides convenient way for working with ModuleFederation runtime changes in runtime;
34
+ */
35
+ function useMFClient(opts) {
36
+ var MFClient = isBrowser
37
+ ? window.mf_client
38
+ : /* TODO: inject here SSR version of MFClient if it will be needed in future */ {};
39
+ var innerState = React__namespace.useRef({
40
+ remote: undefined,
41
+ });
42
+ React__namespace.useEffect(function () {
43
+ // Step 1: Define handlers and helpers
44
+ var processRemoteChange = function (remote) {
45
+ if (innerState.current.remote !== remote) {
46
+ innerState.current.remote = remote;
47
+ if (opts === null || opts === void 0 ? void 0 : opts.onChangeRemote) {
48
+ opts.onChangeRemote(remote, MFClient);
49
+ }
50
+ }
51
+ };
52
+ var handleRouterChange = function (pathname) {
53
+ if (MFClient.isFederatedPathname(pathname)) {
54
+ var remote = MFClient.remotePages.routeToRemote(pathname);
55
+ processRemoteChange(remote);
56
+ }
57
+ else {
58
+ processRemoteChange(undefined);
59
+ }
60
+ };
61
+ // Step 2: run bootstrap logic
62
+ var initialRemote = MFClient.isFederatedPathname(window.location.pathname)
63
+ ? MFClient.remotePages.routeToRemote(window.location.pathname)
64
+ : undefined;
65
+ if (initialRemote) {
66
+ // important for first load to fire `onChangeRemote` with different remote
67
+ // because in innerState by default we assume that used local application
68
+ processRemoteChange(initialRemote);
69
+ }
70
+ // Step 3: Subscribe on events
71
+ singletonRouter__default["default"].events.on('routeChangeStart', handleRouterChange);
72
+ return function () {
73
+ singletonRouter__default["default"].events.off('routeChangeStart', handleRouterChange);
74
+ };
75
+ }, []);
76
+ return MFClient;
77
+ }
78
+
79
+ exports.useMFClient = useMFClient;
@@ -0,0 +1,17 @@
1
+ import { RemoteContainer } from './RemoteContainer';
2
+ declare type UseMFRemoteResult = {
3
+ /** is container loaded or not */
4
+ loaded: boolean;
5
+ /** remote is Lazy, so it will be loaded if getModule(), getContainer() were called */
6
+ remote: RemoteContainer;
7
+ /** Present if error occurs during remote container loading */
8
+ error: Error | undefined;
9
+ };
10
+ /**
11
+ * React hook which provides an access to RemoteContainer in Module Federation
12
+ *
13
+ * @param global - can be a global variable name OR connection string "global@url"
14
+ */
15
+ export declare function useMFRemote(global: string): UseMFRemoteResult;
16
+ export {};
17
+ //# sourceMappingURL=useMFRemote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMFRemote.d.ts","sourceRoot":"","sources":["../../src/client/useMFRemote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,aAAK,iBAAiB,GAAG;IACvB,iCAAiC;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,MAAM,EAAE,eAAe,CAAC;IACxB,8DAA8D;IAC9D,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;CAC1B,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAwC7D"}