@module-federation/nextjs-mf 5.2.2 → 5.3.1
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/lib/ModuleFederationPlugin.js +80 -0
- package/lib/NextFederationPlugin.js +524 -1
- package/lib/_virtual/_tslib.js +101 -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/include-defaults.js +1 -3
- package/lib/internal.js +241 -0
- package/lib/loaders/UrlNode.js +209 -0
- package/lib/loaders/nextPageMapLoader.js +63 -13
- package/lib/loaders/patchNextClientPageLoader.js +53 -0
- package/lib/node-plugin/streaming/CommonJsChunkLoadingPlugin.js +86 -0
- package/lib/node-plugin/streaming/LoadFileChunkLoadingRuntimeModule.js +410 -0
- package/lib/node-plugin/streaming/NodeRuntime.js +147 -0
- package/lib/node-plugin/streaming/index.js +44 -0
- package/lib/node-plugin/streaming/loadScript.js +55 -0
- package/lib/plugins/DevHmrFixInvalidPongPlugin.js +60 -0
- package/lib/utils.js +14 -3
- package/node-plugin/README.md +27 -0
- package/node-plugin/package.json +4 -0
- package/node-plugin/streaming/CommonJsChunkLoadingPlugin.js +89 -0
- package/node-plugin/streaming/LoadFileChunkLoadingRuntimeModule.js +410 -0
- package/node-plugin/streaming/NodeRuntime.js +245 -0
- package/node-plugin/streaming/index.js +42 -0
- package/node-plugin/streaming/loadScript.js +51 -0
- package/package.json +22 -5
- package/tsconfig.json +33 -0
- package/lib/NextFederationPlugin2.js +0 -0
|
@@ -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"}
|
|
@@ -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;
|
package/lib/include-defaults.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// __webpack_init_sharing__('default');
|
|
3
|
-
// }
|
|
1
|
+
// this is needed to ensure webpack does not attempt to tree shake unused modules. Since these should always come from host
|
|
4
2
|
require('react');
|
|
5
3
|
require('react-dom');
|
|
6
4
|
require('next/link');
|
package/lib/internal.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { parseOptions } from 'webpack/lib/container/options';
|
|
2
|
+
import { isRequiredVersion } from 'webpack/lib/sharing/utils';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
// the share scope we attach by default
|
|
6
|
+
// in hosts we re-key them to prevent webpack moving the modules into their own chunks (cause eager error)
|
|
7
|
+
// in remote these are marked as import:false as we always expect the host to prove them
|
|
8
|
+
export const DEFAULT_SHARE_SCOPE = {
|
|
9
|
+
react: {
|
|
10
|
+
singleton: true,
|
|
11
|
+
requiredVersion: false,
|
|
12
|
+
},
|
|
13
|
+
'react/jsx-runtime': {
|
|
14
|
+
singleton: true,
|
|
15
|
+
requiredVersion: false,
|
|
16
|
+
},
|
|
17
|
+
'react-dom': {
|
|
18
|
+
singleton: true,
|
|
19
|
+
requiredVersion: false,
|
|
20
|
+
},
|
|
21
|
+
'next/dynamic': {
|
|
22
|
+
requiredVersion: false,
|
|
23
|
+
singleton: true,
|
|
24
|
+
},
|
|
25
|
+
'styled-jsx': {
|
|
26
|
+
requiredVersion: false,
|
|
27
|
+
singleton: true,
|
|
28
|
+
},
|
|
29
|
+
'next/link': {
|
|
30
|
+
requiredVersion: false,
|
|
31
|
+
singleton: true,
|
|
32
|
+
},
|
|
33
|
+
'next/router': {
|
|
34
|
+
requiredVersion: false,
|
|
35
|
+
singleton: true,
|
|
36
|
+
},
|
|
37
|
+
'next/script': {
|
|
38
|
+
requiredVersion: false,
|
|
39
|
+
singleton: true,
|
|
40
|
+
},
|
|
41
|
+
'next/head': {
|
|
42
|
+
requiredVersion: false,
|
|
43
|
+
singleton: true,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
// put host infront of any shared module key, so "hostreact"
|
|
47
|
+
export const reKeyHostShared = (options) => {
|
|
48
|
+
return Object.entries({
|
|
49
|
+
...(options || {}),
|
|
50
|
+
...DEFAULT_SHARE_SCOPE,
|
|
51
|
+
}).reduce((acc, item) => {
|
|
52
|
+
const [itemKey, shareOptions] = item;
|
|
53
|
+
|
|
54
|
+
const shareKey = 'host' + (item.shareKey || itemKey);
|
|
55
|
+
acc[shareKey] = shareOptions;
|
|
56
|
+
if (!shareOptions.import) {
|
|
57
|
+
acc[shareKey].import = itemKey;
|
|
58
|
+
}
|
|
59
|
+
if (!shareOptions.shareKey) {
|
|
60
|
+
acc[shareKey].shareKey = itemKey;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (DEFAULT_SHARE_SCOPE[itemKey]) {
|
|
64
|
+
acc[shareKey].packageName = itemKey;
|
|
65
|
+
}
|
|
66
|
+
return acc;
|
|
67
|
+
}, {});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// split the @ syntax into url and global
|
|
71
|
+
export const extractUrlAndGlobal = (urlAndGlobal) => {
|
|
72
|
+
const index = urlAndGlobal.indexOf('@');
|
|
73
|
+
if (index <= 0 || index === urlAndGlobal.length - 1) {
|
|
74
|
+
throw new Error(`Invalid request "${urlAndGlobal}"`);
|
|
75
|
+
}
|
|
76
|
+
return [urlAndGlobal.substring(index + 1), urlAndGlobal.substring(0, index)];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// browser template to convert remote into promise new promise and use require.loadChunk to load the chunk
|
|
80
|
+
export const generateRemoteTemplate = (url, global) => {
|
|
81
|
+
return `promise new Promise(function (resolve, reject) {
|
|
82
|
+
var __webpack_error__ = new Error();
|
|
83
|
+
if (typeof ${global} !== 'undefined') return resolve();
|
|
84
|
+
__webpack_require__.l(
|
|
85
|
+
${JSON.stringify(url)},
|
|
86
|
+
function (event) {
|
|
87
|
+
if (typeof ${global} !== 'undefined') return resolve();
|
|
88
|
+
var errorType = event && (event.type === 'load' ? 'missing' : event.type);
|
|
89
|
+
var realSrc = event && event.target && event.target.src;
|
|
90
|
+
__webpack_error__.message =
|
|
91
|
+
'Loading script failed.\\n(' + errorType + ': ' + realSrc + ')';
|
|
92
|
+
__webpack_error__.name = 'ScriptExternalLoadError';
|
|
93
|
+
__webpack_error__.type = errorType;
|
|
94
|
+
__webpack_error__.request = realSrc;
|
|
95
|
+
reject(__webpack_error__);
|
|
96
|
+
},
|
|
97
|
+
${JSON.stringify(global)},
|
|
98
|
+
);
|
|
99
|
+
}).then(function () {
|
|
100
|
+
const proxy = {
|
|
101
|
+
get: ${global}.get,
|
|
102
|
+
init: function(shareScope) {
|
|
103
|
+
const handler = {
|
|
104
|
+
get(target, prop) {
|
|
105
|
+
if (target[prop]) {
|
|
106
|
+
Object.values(target[prop]).forEach(function(o) {
|
|
107
|
+
if(o.from === '_N_E') {
|
|
108
|
+
o.loaded = 1
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
return target[prop]
|
|
113
|
+
},
|
|
114
|
+
set(target, property, value, receiver) {
|
|
115
|
+
if (target[property]) {
|
|
116
|
+
return target[property]
|
|
117
|
+
}
|
|
118
|
+
target[property] = value
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
${global}.init(new Proxy(shareScope, handler))
|
|
124
|
+
} catch (e) {
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
${global}.__initialized = true
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!${global}.__initialized) {
|
|
131
|
+
proxy.init()
|
|
132
|
+
}
|
|
133
|
+
return proxy
|
|
134
|
+
})`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const parseShareOptions = (options) => {
|
|
138
|
+
const sharedOptions = parseOptions(
|
|
139
|
+
options.shared,
|
|
140
|
+
(item, key) => {
|
|
141
|
+
if (typeof item !== 'string')
|
|
142
|
+
throw new Error('Unexpected array in shared');
|
|
143
|
+
/** @type {SharedConfig} */
|
|
144
|
+
const config =
|
|
145
|
+
item === key || !isRequiredVersion(item)
|
|
146
|
+
? {
|
|
147
|
+
import: item,
|
|
148
|
+
}
|
|
149
|
+
: {
|
|
150
|
+
import: key,
|
|
151
|
+
requiredVersion: item,
|
|
152
|
+
};
|
|
153
|
+
return config;
|
|
154
|
+
},
|
|
155
|
+
(item) => item
|
|
156
|
+
);
|
|
157
|
+
return sharedOptions.reduce((acc, [key, options]) => {
|
|
158
|
+
acc[key] = {
|
|
159
|
+
import: options.import,
|
|
160
|
+
shareKey: options.shareKey || key,
|
|
161
|
+
shareScope: options.shareScope,
|
|
162
|
+
requiredVersion: options.requiredVersion,
|
|
163
|
+
strictVersion: options.strictVersion,
|
|
164
|
+
singleton: options.singleton,
|
|
165
|
+
packageName: options.packageName,
|
|
166
|
+
eager: options.eager,
|
|
167
|
+
};
|
|
168
|
+
return acc;
|
|
169
|
+
}, {});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// shared packages must be compiled into webpack bundle, not require() pass through
|
|
173
|
+
export const internalizeSharedPackages = (options, compiler) => {
|
|
174
|
+
//TODO: should use this util for other areas where we read MF options from userland
|
|
175
|
+
if (!options.shared) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const sharedOptions = parseShareOptions(options);
|
|
179
|
+
// get share keys from user, filter out ones that need to be external
|
|
180
|
+
const internalizableKeys = Object.keys(sharedOptions).filter((key) => {
|
|
181
|
+
if (!DEFAULT_SHARE_SCOPE[key]) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (!DEFAULT_SHARE_SCOPE[sharedOptions[key].import]) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// take original externals regex
|
|
189
|
+
const backupExternals = compiler.options.externals[0];
|
|
190
|
+
// if externals is a function (like when you're not running in serverless mode or creating a single build)
|
|
191
|
+
if (typeof backupExternals === 'function') {
|
|
192
|
+
// replace externals function with short-circuit, or fall back to original algo
|
|
193
|
+
compiler.options.externals[0] = (mod, callback) => {
|
|
194
|
+
if (!internalizableKeys.some((v) => mod.request.includes(v))) {
|
|
195
|
+
return backupExternals(mod, callback);
|
|
196
|
+
}
|
|
197
|
+
// bundle it
|
|
198
|
+
return Promise.resolve();
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const externalizedShares = Object.entries(DEFAULT_SHARE_SCOPE).reduce(
|
|
204
|
+
(acc, item) => {
|
|
205
|
+
const [key, value] = item;
|
|
206
|
+
acc[key] = { ...value, import: false };
|
|
207
|
+
if (key === 'react/jsx-runtime') {
|
|
208
|
+
delete acc[key].import;
|
|
209
|
+
}
|
|
210
|
+
return acc;
|
|
211
|
+
},
|
|
212
|
+
{}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// determine output base path, derives .next folder location
|
|
216
|
+
export const getOutputPath = (compiler) => {
|
|
217
|
+
const isServer = compiler.options.target !== 'client';
|
|
218
|
+
let outputPath = compiler.options.output.path.split(path.sep);
|
|
219
|
+
const foundIndex = outputPath.findIndex((i) => {
|
|
220
|
+
return i === (isServer ? 'server' : 'static');
|
|
221
|
+
});
|
|
222
|
+
outputPath = outputPath
|
|
223
|
+
.slice(0, foundIndex > 0 ? foundIndex : outputPath.length)
|
|
224
|
+
.join(path.sep);
|
|
225
|
+
|
|
226
|
+
return outputPath;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const removePlugins = [
|
|
230
|
+
'NextJsRequireCacheHotReloader',
|
|
231
|
+
'BuildManifestPlugin',
|
|
232
|
+
'WellKnownErrorsPlugin',
|
|
233
|
+
'WebpackBuildEventsPlugin',
|
|
234
|
+
'HotModuleReplacementPlugin',
|
|
235
|
+
'NextMiniCssExtractPlugin',
|
|
236
|
+
'NextFederationPlugin',
|
|
237
|
+
'CopyFilePlugin',
|
|
238
|
+
'ProfilingPlugin',
|
|
239
|
+
'DropClientPage',
|
|
240
|
+
'ReactFreshWebpackPlugin',
|
|
241
|
+
];
|