@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.
- package/LICENSE +21 -0
- package/lib/NextFederationPlugin.js +1 -481
- package/lib/NextFederationPlugin2.js +536 -0
- package/lib/_virtual/UrlNode.js +8 -0
- package/lib/_virtual/_commonjsHelpers.js +26 -0
- package/lib/_virtual/_tslib.js +101 -0
- package/lib/_virtual/helpers.js +7 -0
- package/lib/_virtual/nextPageMapLoader.js +7 -0
- package/lib/client/CombinedPages.d.ts +28 -0
- package/lib/client/CombinedPages.d.ts.map +1 -0
- package/lib/client/CombinedPages.js +60 -0
- package/lib/client/MFClient.d.ts +70 -0
- package/lib/client/MFClient.d.ts.map +1 -0
- package/lib/client/MFClient.js +197 -0
- package/lib/client/RemoteContainer.d.ts +58 -0
- package/lib/client/RemoteContainer.d.ts.map +1 -0
- package/lib/client/RemoteContainer.js +161 -0
- package/lib/client/RemotePages.d.ts +48 -0
- package/lib/client/RemotePages.d.ts.map +1 -0
- package/lib/client/RemotePages.js +168 -0
- package/lib/client/UrlNode.d.ts +18 -0
- package/lib/client/UrlNode.d.ts.map +1 -0
- package/lib/client/UrlNode.js +162 -0
- package/lib/client/helpers.d.ts +17 -0
- package/lib/client/helpers.d.ts.map +1 -0
- package/lib/client/helpers.js +108 -0
- package/lib/client/useMFClient.d.ts +25 -0
- package/lib/client/useMFClient.d.ts.map +1 -0
- package/lib/client/useMFClient.js +79 -0
- package/lib/client/useMFRemote.d.ts +17 -0
- package/lib/client/useMFRemote.d.ts.map +1 -0
- package/lib/client/useMFRemote.js +72 -0
- package/lib/loaders/UrlNode.js +215 -0
- package/lib/loaders/fixImageLoader.js +42 -8
- package/lib/loaders/helpers.js +21 -2
- package/lib/loaders/nextPageMapLoader.js +81 -17
- package/lib/loaders/patchNextClientPageLoader.js +53 -0
- package/lib/plugins/DevHmrFixInvalidPongPlugin.js +65 -0
- package/lib/utils.js +7 -3
- package/package.json +21 -4
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _tslib = require('../_virtual/_tslib.js');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var RemoteContainer = require('./RemoteContainer.js');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n["default"] = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
|
|
29
|
+
var isBrowser = typeof window !== 'undefined';
|
|
30
|
+
/**
|
|
31
|
+
* React hook which provides an access to RemoteContainer in Module Federation
|
|
32
|
+
*
|
|
33
|
+
* @param global - can be a global variable name OR connection string "global@url"
|
|
34
|
+
*/
|
|
35
|
+
function useMFRemote(global) {
|
|
36
|
+
var remote;
|
|
37
|
+
if (isBrowser) {
|
|
38
|
+
// on client (we get instances from global variable because webpack breaks Singletons)
|
|
39
|
+
var MFClient_1 = window.mf_client;
|
|
40
|
+
remote = MFClient_1.remotes[global] || MFClient_1.registerRemote(global);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// on server side
|
|
44
|
+
remote = RemoteContainer.RemoteContainer.createSingleton(global);
|
|
45
|
+
}
|
|
46
|
+
var _a = _tslib.__read(React__namespace.useState(remote.isLoaded()), 2), loaded = _a[0], setLoaded = _a[1];
|
|
47
|
+
var _b = _tslib.__read(React__namespace.useState(remote.error), 2), error = _b[0], setError = _b[1];
|
|
48
|
+
React__namespace.useEffect(function () {
|
|
49
|
+
var handleLoadComplete = function () {
|
|
50
|
+
setLoaded(true);
|
|
51
|
+
};
|
|
52
|
+
var handleLoadError = function (e) {
|
|
53
|
+
setError(e);
|
|
54
|
+
};
|
|
55
|
+
if (!loaded && remote.isLoaded()) {
|
|
56
|
+
handleLoadComplete();
|
|
57
|
+
}
|
|
58
|
+
remote.events.on('loadComplete', handleLoadComplete);
|
|
59
|
+
remote.events.on('loadError', handleLoadError);
|
|
60
|
+
return function () {
|
|
61
|
+
remote.events.off('loadComplete', handleLoadComplete);
|
|
62
|
+
remote.events.off('loadError', handleLoadError);
|
|
63
|
+
};
|
|
64
|
+
}, [remote]);
|
|
65
|
+
return {
|
|
66
|
+
remote: remote,
|
|
67
|
+
loaded: loaded,
|
|
68
|
+
error: error,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.useMFRemote = useMFRemote;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 🛑🛑🛑 Attention! 🛑🛑🛑
|
|
7
|
+
* Do not add type definitions to this file!!
|
|
8
|
+
* It already exists in src/client folder.
|
|
9
|
+
* So remove this file and import ts version from src/client/UrlNode.
|
|
10
|
+
*
|
|
11
|
+
* This file was add just for proper compilation of JS files without any rollup warnings:
|
|
12
|
+
* "(!) Unresolved dependencies"
|
|
13
|
+
* "nextjs-mf/src/client/UrlNode (imported by nextjs-mf/src/client/UrlNode?commonjs-external)"
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This class provides a logic of sorting dynamic routes in NextJS.
|
|
18
|
+
*
|
|
19
|
+
* It was copied from
|
|
20
|
+
* @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
|
|
21
|
+
*/
|
|
22
|
+
class UrlNode {
|
|
23
|
+
placeholder = true;
|
|
24
|
+
children = new Map();
|
|
25
|
+
slugName = null;
|
|
26
|
+
restSlugName = null;
|
|
27
|
+
optionalRestSlugName = null;
|
|
28
|
+
|
|
29
|
+
insert(urlPath) {
|
|
30
|
+
this._insert(urlPath.split('/').filter(Boolean), [], false);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
smoosh() {
|
|
34
|
+
return this._smoosh();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_smoosh(prefix = '/') {
|
|
38
|
+
const childrenPaths = [...this.children.keys()].sort();
|
|
39
|
+
if (this.slugName !== null) {
|
|
40
|
+
childrenPaths.splice(childrenPaths.indexOf('[]'), 1);
|
|
41
|
+
}
|
|
42
|
+
if (this.restSlugName !== null) {
|
|
43
|
+
childrenPaths.splice(childrenPaths.indexOf('[...]'), 1);
|
|
44
|
+
}
|
|
45
|
+
if (this.optionalRestSlugName !== null) {
|
|
46
|
+
childrenPaths.splice(childrenPaths.indexOf('[[...]]'), 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const routes = childrenPaths
|
|
50
|
+
.map((c) => this.children.get(c)._smoosh(`${prefix}${c}/`))
|
|
51
|
+
.reduce((prev, curr) => [...prev, ...curr], []);
|
|
52
|
+
|
|
53
|
+
if (this.slugName !== null) {
|
|
54
|
+
routes.push(
|
|
55
|
+
...this.children.get('[]')._smoosh(`${prefix}[${this.slugName}]/`)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!this.placeholder) {
|
|
60
|
+
const r = prefix === '/' ? '/' : prefix.slice(0, -1);
|
|
61
|
+
if (this.optionalRestSlugName != null) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`You cannot define a route with the same specificity as a optional catch-all route ("${r}" and "${r}[[...${this.optionalRestSlugName}]]").`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
routes.unshift(r);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.restSlugName !== null) {
|
|
71
|
+
routes.push(
|
|
72
|
+
...this.children
|
|
73
|
+
.get('[...]')
|
|
74
|
+
._smoosh(`${prefix}[...${this.restSlugName}]/`)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.optionalRestSlugName !== null) {
|
|
79
|
+
routes.push(
|
|
80
|
+
...this.children
|
|
81
|
+
.get('[[...]]')
|
|
82
|
+
._smoosh(`${prefix}[[...${this.optionalRestSlugName}]]/`)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return routes;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_insert(urlPaths, slugNames, isCatchAll) {
|
|
90
|
+
if (urlPaths.length === 0) {
|
|
91
|
+
this.placeholder = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isCatchAll) {
|
|
96
|
+
throw new Error(`Catch-all must be the last part of the URL.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The next segment in the urlPaths list
|
|
100
|
+
let nextSegment = urlPaths[0];
|
|
101
|
+
|
|
102
|
+
// Check if the segment matches `[something]`
|
|
103
|
+
if (nextSegment.startsWith('[') && nextSegment.endsWith(']')) {
|
|
104
|
+
// Strip `[` and `]`, leaving only `something`
|
|
105
|
+
let segmentName = nextSegment.slice(1, -1);
|
|
106
|
+
|
|
107
|
+
let isOptional = false;
|
|
108
|
+
if (segmentName.startsWith('[') && segmentName.endsWith(']')) {
|
|
109
|
+
// Strip optional `[` and `]`, leaving only `something`
|
|
110
|
+
segmentName = segmentName.slice(1, -1);
|
|
111
|
+
isOptional = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (segmentName.startsWith('...')) {
|
|
115
|
+
// Strip `...`, leaving only `something`
|
|
116
|
+
segmentName = segmentName.substring(3);
|
|
117
|
+
isCatchAll = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (segmentName.startsWith('[') || segmentName.endsWith(']')) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Segment names may not start or end with extra brackets ('${segmentName}').`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (segmentName.startsWith('.')) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Segment names may not start with erroneous periods ('${segmentName}').`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const handleSlug = function handleSlug(previousSlug, nextSlug) {
|
|
133
|
+
if (previousSlug !== null) {
|
|
134
|
+
// If the specific segment already has a slug but the slug is not `something`
|
|
135
|
+
// This prevents collisions like:
|
|
136
|
+
// pages/[post]/index.js
|
|
137
|
+
// pages/[id]/index.js
|
|
138
|
+
// Because currently multiple dynamic params on the same segment level are not supported
|
|
139
|
+
if (previousSlug !== nextSlug) {
|
|
140
|
+
// TODO: This error seems to be confusing for users, needs an error link, the description can be based on above comment.
|
|
141
|
+
throw new Error(
|
|
142
|
+
`You cannot use different slug names for the same dynamic path ('${previousSlug}' !== '${nextSlug}').`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
slugNames.forEach((slug) => {
|
|
148
|
+
if (slug === nextSlug) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`You cannot have the same slug name "${nextSlug}" repeat within a single dynamic path`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (slug.replace(/\W/g, '') === nextSegment.replace(/\W/g, '')) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`You cannot have the slug names "${slug}" and "${nextSlug}" differ only by non-word symbols within a single dynamic path`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
slugNames.push(nextSlug);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
if (isCatchAll) {
|
|
165
|
+
if (isOptional) {
|
|
166
|
+
if (this.restSlugName != null) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`You cannot use both an required and optional catch-all route at the same level ("[...${this.restSlugName}]" and "${urlPaths[0]}" ).`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
handleSlug(this.optionalRestSlugName, segmentName);
|
|
173
|
+
// slugName is kept as it can only be one particular slugName
|
|
174
|
+
this.optionalRestSlugName = segmentName;
|
|
175
|
+
// nextSegment is overwritten to [[...]] so that it can later be sorted specifically
|
|
176
|
+
nextSegment = '[[...]]';
|
|
177
|
+
} else {
|
|
178
|
+
if (this.optionalRestSlugName != null) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`You cannot use both an optional and required catch-all route at the same level ("[[...${this.optionalRestSlugName}]]" and "${urlPaths[0]}").`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
handleSlug(this.restSlugName, segmentName);
|
|
185
|
+
// slugName is kept as it can only be one particular slugName
|
|
186
|
+
this.restSlugName = segmentName;
|
|
187
|
+
// nextSegment is overwritten to [...] so that it can later be sorted specifically
|
|
188
|
+
nextSegment = '[...]';
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
if (isOptional) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Optional route parameters are not yet supported ("${urlPaths[0]}").`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
handleSlug(this.slugName, segmentName);
|
|
197
|
+
// slugName is kept as it can only be one particular slugName
|
|
198
|
+
this.slugName = segmentName;
|
|
199
|
+
// nextSegment is overwritten to [] so that it can later be sorted specifically
|
|
200
|
+
nextSegment = '[]';
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// If this UrlNode doesn't have the nextSegment yet we create a new child UrlNode
|
|
205
|
+
if (!this.children.has(nextSegment)) {
|
|
206
|
+
this.children.set(nextSegment, new UrlNode());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.children
|
|
210
|
+
.get(nextSegment)
|
|
211
|
+
._insert(urlPaths.slice(1), slugNames, isCatchAll);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
exports.UrlNode = UrlNode;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const Template = require('webpack/lib/Template');
|
|
3
|
+
const RuntimeGlobals = require('webpack/lib/RuntimeGlobals');
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* This loader was specially created for tunning next-image-loader result
|
|
3
7
|
* see https://github.com/vercel/next.js/blob/canary/packages/next/build/webpack/loaders/next-image-loader.js
|
|
@@ -12,14 +16,44 @@
|
|
|
12
16
|
*
|
|
13
17
|
* @type {(this: import("webpack").LoaderContext<{}>, content: string) => string>}
|
|
14
18
|
*/
|
|
15
|
-
function fixImageLoader(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
async function fixImageLoader(remaining) {
|
|
20
|
+
this.cacheable(true);
|
|
21
|
+
const isServer = this._compiler.options.name !== 'client';
|
|
22
|
+
const result = await this.importModule(
|
|
23
|
+
`${this.resourcePath}.webpack[javascript/auto]` + `!=!${remaining}`
|
|
24
|
+
);
|
|
25
|
+
const content = result.default || result;
|
|
26
|
+
|
|
27
|
+
const computedAssetPrefix = isServer
|
|
28
|
+
? ` \'\'`
|
|
29
|
+
: `(${RuntimeGlobals.publicPath} && ${RuntimeGlobals.publicPath}.indexOf('://') > 0 ? new URL(${RuntimeGlobals.publicPath}).origin : \'\')`;
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
const constructedObject = Object.entries(content).reduce(
|
|
32
|
+
(acc, [key, value]) => {
|
|
33
|
+
if (key === 'src') {
|
|
34
|
+
if (value && value.indexOf('://') < 0) {
|
|
35
|
+
value = path.join(value);
|
|
36
|
+
}
|
|
37
|
+
acc.push(
|
|
38
|
+
`${key}: computedAssetsPrefixReference + ${JSON.stringify(value)}`
|
|
39
|
+
);
|
|
40
|
+
return acc;
|
|
41
|
+
}
|
|
42
|
+
acc.push(`${key}: ${JSON.stringify(value)}`);
|
|
43
|
+
return acc;
|
|
44
|
+
},
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
const updated = Template.asString([
|
|
48
|
+
"let computedAssetsPrefixReference = '';",
|
|
49
|
+
'try {',
|
|
50
|
+
Template.indent(`computedAssetsPrefixReference = ${computedAssetPrefix};`),
|
|
51
|
+
'} catch (e) {}',
|
|
52
|
+
'export default {',
|
|
53
|
+
Template.indent(constructedObject.join(',\n')),
|
|
54
|
+
'}',
|
|
55
|
+
]);
|
|
56
|
+
return updated;
|
|
23
57
|
}
|
|
24
58
|
|
|
25
|
-
module.exports = fixImageLoader;
|
|
59
|
+
module.exports.pitch = fixImageLoader;
|
package/lib/loaders/helpers.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var helpers = require('../_virtual/helpers.js');
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Inject a loader into the current module rule.
|
|
3
7
|
* This function mutates `rule` argument!
|
|
4
8
|
*/
|
|
5
|
-
|
|
9
|
+
|
|
10
|
+
helpers.__exports.injectRuleLoader = function injectRuleLoader(rule, loader) {
|
|
6
11
|
if (rule.loader) {
|
|
7
12
|
rule.use = [loader, { loader: rule.loader, options: rule.options }];
|
|
8
13
|
delete rule.loader;
|
|
@@ -15,7 +20,7 @@ module.exports.injectRuleLoader = function injectRuleLoader(rule, loader) {
|
|
|
15
20
|
/**
|
|
16
21
|
* Check that current module rule has a loader with the provided name.
|
|
17
22
|
*/
|
|
18
|
-
|
|
23
|
+
helpers.__exports.hasLoader = function hasLoader(rule, loaderName) {
|
|
19
24
|
if (rule.loader === loaderName) {
|
|
20
25
|
return true;
|
|
21
26
|
} else if (rule.use) {
|
|
@@ -33,3 +38,17 @@ module.exports.hasLoader = function hasLoader(rule, loaderName) {
|
|
|
33
38
|
}
|
|
34
39
|
return false;
|
|
35
40
|
};
|
|
41
|
+
|
|
42
|
+
helpers.__exports.toDisplayErrors = function toDisplayErrors(err) {
|
|
43
|
+
return err
|
|
44
|
+
.map((error) => {
|
|
45
|
+
let message = error.message;
|
|
46
|
+
if (error.stack) {
|
|
47
|
+
message += '\n' + error.stack;
|
|
48
|
+
}
|
|
49
|
+
return message;
|
|
50
|
+
})
|
|
51
|
+
.join('\n');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = helpers.__exports;
|
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var nextPageMapLoader$1 = require('../_virtual/nextPageMapLoader.js');
|
|
4
|
+
var require$$0 = require('fast-glob');
|
|
5
|
+
var require$$1 = require('fs');
|
|
6
|
+
var UrlNode$1 = require('../_virtual/UrlNode.js');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
+
|
|
10
|
+
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
|
|
11
|
+
var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1);
|
|
12
|
+
|
|
13
|
+
const fg = require$$0__default["default"];
|
|
14
|
+
const fs = require$$1__default["default"];
|
|
15
|
+
|
|
16
|
+
// TODO: import UrlNode from ./client folder when whole project migrates on TypeScript (but right now using JS copy of this class)
|
|
17
|
+
// const UrlNode = require('../client/UrlNode').UrlNode;
|
|
18
|
+
const UrlNode = UrlNode$1.UrlNode;
|
|
3
19
|
|
|
4
20
|
/**
|
|
5
21
|
* Webpack loader which prepares MF map for NextJS pages
|
|
@@ -7,17 +23,22 @@ const fs = require('fs');
|
|
|
7
23
|
* @type {(this: import("webpack").LoaderContext<{}>, content: string) => string>}
|
|
8
24
|
*/
|
|
9
25
|
function nextPageMapLoader() {
|
|
10
|
-
const pages = getNextPages(this.rootContext);
|
|
11
|
-
const pageMap = preparePageMap(pages);
|
|
12
|
-
|
|
13
26
|
// const [pagesRoot] = getNextPagesRoot(this.rootContext);
|
|
14
27
|
// this.addContextDependency(pagesRoot);
|
|
28
|
+
const opts = this.getOptions();
|
|
29
|
+
const pages = getNextPages(this.rootContext);
|
|
15
30
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
let result = '';
|
|
32
|
+
if (Object.hasOwnProperty.call(opts, 'v2')) {
|
|
33
|
+
result = preparePageMapV2(pages);
|
|
34
|
+
} else {
|
|
35
|
+
result = preparePageMap(pages);
|
|
36
|
+
}
|
|
19
37
|
|
|
20
|
-
this.callback(
|
|
38
|
+
this.callback(
|
|
39
|
+
null,
|
|
40
|
+
`module.exports = { default: ${JSON.stringify(result)} };`
|
|
41
|
+
);
|
|
21
42
|
}
|
|
22
43
|
|
|
23
44
|
/**
|
|
@@ -38,6 +59,7 @@ function exposeNextjsPages(cwd) {
|
|
|
38
59
|
|
|
39
60
|
const exposesWithPageMap = {
|
|
40
61
|
'./pages-map': `${__filename}!${__filename}`,
|
|
62
|
+
'./pages-map-v2': `${__filename}?v2!${__filename}`,
|
|
41
63
|
...pageModulesMap,
|
|
42
64
|
};
|
|
43
65
|
|
|
@@ -113,17 +135,59 @@ function sanitizePagePath(item) {
|
|
|
113
135
|
function preparePageMap(pages) {
|
|
114
136
|
const result = {};
|
|
115
137
|
|
|
116
|
-
pages.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
const clearedPages = pages.map((p) => `/${sanitizePagePath(p)}`);
|
|
139
|
+
|
|
140
|
+
// getSortedRoutes @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
|
|
141
|
+
const root = new UrlNode();
|
|
142
|
+
clearedPages.forEach((pagePath) => root.insert(pagePath));
|
|
143
|
+
// Smoosh will then sort those sublevels up to the point where you get the correct route definition priority
|
|
144
|
+
const sortedPages = root.smoosh();
|
|
145
|
+
|
|
146
|
+
sortedPages.forEach((page) => {
|
|
147
|
+
let key = page
|
|
148
|
+
.replace(/\[\.\.\.[^\]]+\]/gi, '*')
|
|
149
|
+
.replace(/\[([^\]]+)\]/gi, ':$1');
|
|
121
150
|
key = key.replace(/^\/pages\//, '/').replace(/\/index$/, '') || '/';
|
|
122
|
-
result[key] =
|
|
151
|
+
result[key] = `.${page}`;
|
|
123
152
|
});
|
|
124
153
|
|
|
125
154
|
return result;
|
|
126
155
|
}
|
|
127
156
|
|
|
128
|
-
|
|
129
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Create MF list of NextJS pages
|
|
159
|
+
*
|
|
160
|
+
* From
|
|
161
|
+
* ['pages/index.tsx', 'pages/storage/[...slug].tsx', 'pages/storage/index.tsx']
|
|
162
|
+
* Getting the following map
|
|
163
|
+
* {
|
|
164
|
+
* '/': './pages/index',
|
|
165
|
+
* '/storage': './pages/storage/index'
|
|
166
|
+
* '/storage/[...slug]': './pages/storage/[...slug]',
|
|
167
|
+
* }
|
|
168
|
+
*
|
|
169
|
+
* @type {(pages: string[]) => {[key: string]: string}}
|
|
170
|
+
*/
|
|
171
|
+
function preparePageMapV2(pages) {
|
|
172
|
+
const result = {};
|
|
173
|
+
|
|
174
|
+
const clearedPages = pages.map((p) => `/${sanitizePagePath(p)}`);
|
|
175
|
+
|
|
176
|
+
// getSortedRoutes @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
|
|
177
|
+
const root = new UrlNode();
|
|
178
|
+
clearedPages.forEach((pagePath) => root.insert(pagePath));
|
|
179
|
+
// Smoosh will then sort those sublevels up to the point where you get the correct route definition priority
|
|
180
|
+
const sortedPages = root.smoosh();
|
|
181
|
+
|
|
182
|
+
sortedPages.forEach((page) => {
|
|
183
|
+
let key = page.replace(/^\/pages\//, '/').replace(/\/index$/, '') || '/';
|
|
184
|
+
result[key] = `.${page}`;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
nextPageMapLoader$1.nextPageMapLoader.exports = nextPageMapLoader;
|
|
191
|
+
nextPageMapLoader$1.nextPageMapLoader.exports.exposeNextjsPages = exposeNextjsPages;
|
|
192
|
+
|
|
193
|
+
module.exports = nextPageMapLoader$1.nextPageMapLoader.exports;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This webpack loader patches next/dist/client/page-loader.js file.
|
|
5
|
+
* Also it requires `include-defaults.js` with required shared libs
|
|
6
|
+
*
|
|
7
|
+
* @type {(this: import("webpack").LoaderContext<{}>, content: string) => string>}
|
|
8
|
+
*/
|
|
9
|
+
function patchNextClientPageLoader(content) {
|
|
10
|
+
if (content.includes('MFClient')) {
|
|
11
|
+
// If MFClient already applied then skip patch
|
|
12
|
+
return content;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// avoid absolute paths as they break hashing when the root for the project is moved
|
|
16
|
+
// @see https://webpack.js.org/contribute/writing-a-loader/#absolute-paths
|
|
17
|
+
const pathIncludeDefaults = path.relative(
|
|
18
|
+
this.context,
|
|
19
|
+
path.resolve(__dirname, '../include-defaults.js')
|
|
20
|
+
);
|
|
21
|
+
const pathMFClient = path.relative(
|
|
22
|
+
this.context,
|
|
23
|
+
path.resolve(__dirname, '../client/MFClient.js')
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
patchedContent = content.replace(
|
|
27
|
+
'exports.default = PageLoader;',
|
|
28
|
+
`
|
|
29
|
+
require(${JSON.stringify(pathIncludeDefaults)});
|
|
30
|
+
const MFClient = require(${JSON.stringify(pathMFClient)}).MFClient;
|
|
31
|
+
|
|
32
|
+
class PageLoaderExtended extends PageLoader {
|
|
33
|
+
constructor(buildId, assetPrefix) {
|
|
34
|
+
super(buildId, assetPrefix);
|
|
35
|
+
global.mf_client = new MFClient(this);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_getPageListOriginal() {
|
|
39
|
+
return super.getPageList();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getPageList() {
|
|
43
|
+
return global.mf_client.getPageList();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.default = PageLoaderExtended;
|
|
47
|
+
`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return patchedContent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = patchNextClientPageLoader;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* If HMR through websocket received {"invalid":true, "event":"pong"} event
|
|
5
|
+
* then pages reloads. But for federated page this is unwanted behavior.
|
|
6
|
+
*
|
|
7
|
+
* So this plugin in DEV mode disables page.reload() in HMR for federated pages.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class DevHmrFixInvalidPongPlugin {
|
|
11
|
+
/**
|
|
12
|
+
* Apply the plugin
|
|
13
|
+
* @param {import("webpack").Compiler} compiler the compiler instance
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
apply(compiler) {
|
|
17
|
+
const webpack = compiler.webpack;
|
|
18
|
+
|
|
19
|
+
compiler.hooks.thisCompilation.tap(
|
|
20
|
+
'DevHmrFixInvalidPongPlugin',
|
|
21
|
+
(compilation) => {
|
|
22
|
+
compilation.hooks.processAssets.tap(
|
|
23
|
+
{
|
|
24
|
+
name: 'DevHmrFixInvalidPongPlugin',
|
|
25
|
+
state: compilation.constructor.PROCESS_ASSETS_STAGE_PRE_PROCESS,
|
|
26
|
+
},
|
|
27
|
+
(assets) => {
|
|
28
|
+
Object.keys(assets).forEach((filename) => {
|
|
29
|
+
if (filename.endsWith('/main.js')) {
|
|
30
|
+
const asset = compilation.getAsset(filename);
|
|
31
|
+
const newSource = asset.source.source().replace(
|
|
32
|
+
new RegExp(
|
|
33
|
+
escapeRegExp(
|
|
34
|
+
'if (payload.event === \\"pong\\" && payload.invalid && !self.__NEXT_DATA__.err) {'
|
|
35
|
+
),
|
|
36
|
+
'g'
|
|
37
|
+
),
|
|
38
|
+
`if (payload.event === \\"pong\\" && payload.invalid && !self.__NEXT_DATA__.err) {
|
|
39
|
+
if (window.mf_client && window.mf_client.isFederatedPathname(window.location.pathname)) return;
|
|
40
|
+
`.replaceAll('\n', '\\n')
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const updatedAsset = new webpack.sources.RawSource(newSource);
|
|
44
|
+
|
|
45
|
+
if (asset) {
|
|
46
|
+
compilation.updateAsset(filename, updatedAsset);
|
|
47
|
+
} else {
|
|
48
|
+
compilation.emitAsset(filename, updatedAsset);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function escapeRegExp(string) {
|
|
60
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
var DevHmrFixInvalidPongPlugin_1 = DevHmrFixInvalidPongPlugin;
|
|
64
|
+
|
|
65
|
+
module.exports = DevHmrFixInvalidPongPlugin_1;
|
package/lib/utils.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
1
5
|
const remoteVars = process.env.REMOTES || {};
|
|
2
6
|
|
|
3
7
|
const runtimeRemotes = Object.entries(remoteVars).reduce(function (acc, item) {
|
|
@@ -13,8 +17,6 @@ const runtimeRemotes = Object.entries(remoteVars).reduce(function (acc, item) {
|
|
|
13
17
|
return acc;
|
|
14
18
|
}, {});
|
|
15
19
|
|
|
16
|
-
module.exports.remotes = runtimeRemotes;
|
|
17
|
-
|
|
18
20
|
/**
|
|
19
21
|
* Return initialized remote container by remote's key or its runtime remote item data.
|
|
20
22
|
*
|
|
@@ -94,4 +96,6 @@ function injectScript(keyOrRuntimeRemoteItem) {
|
|
|
94
96
|
});
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
var injectScript_1 = injectScript;
|
|
100
|
+
|
|
101
|
+
exports.injectScript = injectScript_1;
|