@module-federation/nextjs-mf 5.2.2 → 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 (38) hide show
  1. package/lib/NextFederationPlugin2.js +0 -0
  2. package/lib/_virtual/UrlNode.js +8 -0
  3. package/lib/_virtual/_commonjsHelpers.js +26 -0
  4. package/lib/_virtual/_tslib.js +101 -0
  5. package/lib/_virtual/helpers.js +7 -0
  6. package/lib/_virtual/nextPageMapLoader.js +7 -0
  7. package/lib/client/CombinedPages.d.ts +28 -0
  8. package/lib/client/CombinedPages.d.ts.map +1 -0
  9. package/lib/client/CombinedPages.js +60 -0
  10. package/lib/client/MFClient.d.ts +70 -0
  11. package/lib/client/MFClient.d.ts.map +1 -0
  12. package/lib/client/MFClient.js +197 -0
  13. package/lib/client/RemoteContainer.d.ts +58 -0
  14. package/lib/client/RemoteContainer.d.ts.map +1 -0
  15. package/lib/client/RemoteContainer.js +161 -0
  16. package/lib/client/RemotePages.d.ts +48 -0
  17. package/lib/client/RemotePages.d.ts.map +1 -0
  18. package/lib/client/RemotePages.js +168 -0
  19. package/lib/client/UrlNode.d.ts +18 -0
  20. package/lib/client/UrlNode.d.ts.map +1 -0
  21. package/lib/client/UrlNode.js +162 -0
  22. package/lib/client/helpers.d.ts +17 -0
  23. package/lib/client/helpers.d.ts.map +1 -0
  24. package/lib/client/helpers.js +108 -0
  25. package/lib/client/useMFClient.d.ts +25 -0
  26. package/lib/client/useMFClient.d.ts.map +1 -0
  27. package/lib/client/useMFClient.js +79 -0
  28. package/lib/client/useMFRemote.d.ts +17 -0
  29. package/lib/client/useMFRemote.d.ts.map +1 -0
  30. package/lib/client/useMFRemote.js +72 -0
  31. package/lib/loaders/UrlNode.js +215 -0
  32. package/lib/loaders/helpers.js +10 -3
  33. package/lib/loaders/nextPageMapLoader.js +81 -17
  34. package/lib/loaders/patchNextClientPageLoader.js +53 -0
  35. package/lib/plugins/DevHmrFixInvalidPongPlugin.js +65 -0
  36. package/lib/utils.js +7 -3
  37. package/package.json +21 -4
  38. package/tsconfig.json +33 -0
@@ -1,5 +1,21 @@
1
- const fg = require('fast-glob');
2
- const fs = require('fs');
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
- const result = `module.exports = {
17
- default: ${JSON.stringify(pageMap)},
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(null, result);
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.forEach((pagePath) => {
117
- const page = sanitizePagePath(pagePath);
118
- let key =
119
- '/' +
120
- page.replace(/\[\.\.\.[^\]]+\]/gi, '*').replace(/\[([^\]]+)\]/gi, ':$1');
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] = `./${page}`;
151
+ result[key] = `.${page}`;
123
152
  });
124
153
 
125
154
  return result;
126
155
  }
127
156
 
128
- module.exports = nextPageMapLoader;
129
- module.exports.exposeNextjsPages = exposeNextjsPages;
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
- module.exports.injectScript = injectScript;
99
+ var injectScript_1 = injectScript;
100
+
101
+ exports.injectScript = injectScript_1;
package/package.json CHANGED
@@ -1,33 +1,50 @@
1
1
  {
2
2
  "public": true,
3
3
  "name": "@module-federation/nextjs-mf",
4
- "version": "5.2.2",
4
+ "version": "5.3.0",
5
5
  "description": "Module Federation helper for NextJS",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "repository": "https://github.com/module-federation/nextjs-mf",
9
9
  "author": "Zack Jackson <zackary.l.jackson@gmail.com>",
10
+ "contributors": [
11
+ "Pavel Chertorogov, nodkz <pavel.chertorogov@gmail.com> (www.ps.kz)"
12
+ ],
10
13
  "license": "MIT",
11
14
  "scripts": {
12
- "demo": "yarn build && cd demo && yarn install && yarn dev",
15
+ "demo": "sleep 3 && cd demo && yarn install && yarn dev",
16
+ "dev": "rm -rf lib && concurrently \"yarn sync-files --watch\" \"yarn compile --watch\" \"yarn demo\"",
13
17
  "prettier": "prettier --write \"**/*.{js,json,md,ts,tsx}\"",
14
- "build": "rm -rf lib && cp -r ./src/ lib/ && rollup -c",
18
+ "sync-files": "cpx \"src/**/*.js\" lib/",
19
+ "compile": "rollup -c",
20
+ "build": "rm -rf lib && yarn sync-files && yarn compile",
15
21
  "prepublishOnly": "yarn build"
16
22
  },
17
23
  "dependencies": {
18
24
  "chalk": "^4.0.0",
25
+ "eventemitter3": "^4.0.7",
19
26
  "fast-glob": "^3.2.11"
20
27
  },
21
28
  "devDependencies": {
22
29
  "@rollup/plugin-commonjs": "^22.0.2",
23
30
  "@rollup/plugin-multi-entry": "^4.1.0",
24
31
  "@rollup/plugin-node-resolve": "^13.3.0",
25
- "next": "11.0.1",
32
+ "@types/react": "^18.0.19",
33
+ "concurrently": "^7.3.0",
34
+ "cpx": "^1.5.0",
35
+ "next": "12.2.2",
26
36
  "prettier": "2.3.2",
37
+ "react": "^18.2.0",
27
38
  "rollup": "^2.78.1",
28
39
  "rollup-obfuscator": "^3.0.1",
29
40
  "rollup-plugin-node-builtins": "^2.1.2",
30
41
  "rollup-plugin-node-globals": "^1.4.0",
42
+ "rollup-plugin-typescript2": "^0.33.0",
43
+ "tslib": "^2.4.0",
44
+ "typescript": "^4.8.2",
31
45
  "webpack": "5.45.1"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^17.0.0 || ^18.0.0"
32
49
  }
33
50
  }
package/tsconfig.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "module": "ES2020",
7
+ "esModuleInterop": true,
8
+ "moduleResolution": "node",
9
+ "sourceMap": true,
10
+ "downlevelIteration": true,
11
+ "resolveJsonModule": true,
12
+ "strict": false,
13
+ "allowJs": true,
14
+ "strictBindCallApply": true,
15
+ "strictNullChecks": true,
16
+ "strictPropertyInitialization": false,
17
+ "suppressImplicitAnyIndexErrors": true,
18
+ "importHelpers": false,
19
+ "noImplicitAny": true,
20
+ "noImplicitReturns": true,
21
+ "noImplicitThis": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUnusedParameters": false,
24
+ "noUnusedLocals": false,
25
+ "noEmitOnError": false,
26
+ "forceConsistentCasingInFileNames": true,
27
+ "skipLibCheck": true,
28
+ "lib": ["dom", "ESNext"],
29
+ "jsx": "react",
30
+ },
31
+ "exclude": ["node_modules"],
32
+ "include": ["src/client/**/*"]
33
+ }