From ae7fc7dc46492adcd6da81711488be4534016246 Mon Sep 17 00:00:00 2001 From: pshu Date: Tue, 9 May 2023 17:30:25 +0800 Subject: [PATCH] =?UTF-8?q?wip:=20fix(plugin-mf):=20=F0=9F=90=9B=20=20re-e?= =?UTF-8?q?xport=20umi.ts=20exports=20for=20qiankun=20(#11109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(plugin-mf): 🐛 re-export umi.ts exports for qiankun * fix: 🐛 unit test * fix: 🐛 change umi entry when in produciton (umi build) * chore: 🙈 cypress videos snapshots * test: ✅ add e2e for mf-host and qiankun-slave * docs: 📝 文档更新 --------- Co-authored-by: pshu --- .gitignore | 2 + docs/docs/max/mf.md | 4 ++ examples/mf-host/.umirc.prod.ts | 3 - examples/mf-host/.umirc.ts | 7 +++ examples/mf-host/package.json | 4 +- examples/mf-remote/.umirc.ts | 1 + examples/qiankun-master/.umirc.ts | 8 +++ examples/qiankun-master/package.json | 4 +- examples/qiankun-slave-app2/.umirc.ts | 1 + examples/qiankun-slave-app2/package.json | 2 + examples/qiankun-slave/.umirc.ts | 1 + .../qiankun-slave/cypress/e2e/example.cy.ts | 11 +++- examples/qiankun-slave/package.json | 11 +++- packages/plugins/src/mf.test.ts | 1 + packages/plugins/src/mf.ts | 59 +++++++++++++++---- 15 files changed, 98 insertions(+), 21 deletions(-) delete mode 100644 examples/mf-host/.umirc.prod.ts diff --git a/.gitignore b/.gitignore index cb1830ae0521..104b34d25ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ /packages/umi/client/**/*.d.ts /packages/umi/client/**/*.d.ts.map /packages/bundler-vite/src/fixtures/alias/node_modules +**/cypress/screenshots/ +**/cypress/videos/ \ No newline at end of file diff --git a/docs/docs/max/mf.md b/docs/docs/max/mf.md index e19e996da092..4c401634e89c 100644 --- a/docs/docs/max/mf.md +++ b/docs/docs/max/mf.md @@ -4,6 +4,10 @@ import { Tabbed, Message } from 'umi'; 在 Umi 项目使用 Module Federation 功能。 + +Module Federation 功能需要浏览器支持 `Top Level Await` 特性。在生产环境中使用请注意浏览器是否支持([浏览器支持情况](https://caniuse.com/?search=top%20level%20await))。 + + ## 配置 ### 使用远端模块配置 diff --git a/examples/mf-host/.umirc.prod.ts b/examples/mf-host/.umirc.prod.ts deleted file mode 100644 index f347438e4497..000000000000 --- a/examples/mf-host/.umirc.prod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default { - mfsu: false, -}; diff --git a/examples/mf-host/.umirc.ts b/examples/mf-host/.umirc.ts index 116130e0ca79..f676d4141a21 100644 --- a/examples/mf-host/.umirc.ts +++ b/examples/mf-host/.umirc.ts @@ -12,6 +12,11 @@ const shared = { }; export default defineConfig({ + publicPath: 'http://localhost:8000/', + qiankun: { + slave: {}, + }, + base: '/', mf: { name: 'hostUser', remotes: [ @@ -31,4 +36,6 @@ export default defineConfig({ ], shared, }, + // dont use in production!!! just for tests + hash: false, }); diff --git a/examples/mf-host/package.json b/examples/mf-host/package.json index 3a6296191a0a..5d15d8db6d54 100644 --- a/examples/mf-host/package.json +++ b/examples/mf-host/package.json @@ -6,13 +6,13 @@ "ci": "cypress run", "cypress:ci": "pnpm umi-scripts cypress", "cypress:open": "cypress open", - "dev": "max dev", + "dev": "cross-env PORT=8000 max dev", "e2e": "cypress run", "e2e:ci": "npm run e2e:ci:dev&&npm run e2e:ci:prod", "e2e:ci:dev": "cross-env PORT=8000 start-server-and-test dev http://127.0.0.1:8000/__umi/api/status start-remote-dev http://127.0.0.1:9000/__umi/api/status cypress:ci", "e2e:ci:local": "cross-env PORT=8000 start-server-and-test dev http://127.0.0.1:8000/__umi/api/status start-remote-dev http://127.0.0.1:9000/__umi/api/status cypress:open", "e2e:ci:prod": "cross-env PORT=8000 start-server-and-test dev http://127.0.0.1:8000/__umi/api/status start-remote-prod 9000 cypress:ci", - "preview": "max preview --port 9527", + "preview": "max preview --port 8000", "start": "npm run dev", "start-remote-dev": "pnpm run --dir ../mf-remote dev", "start-remote-prod": "pnpm run --dir ../mf-remote preview" diff --git a/examples/mf-remote/.umirc.ts b/examples/mf-remote/.umirc.ts index e6d4209297ca..0c181dd3f663 100644 --- a/examples/mf-remote/.umirc.ts +++ b/examples/mf-remote/.umirc.ts @@ -23,4 +23,5 @@ export default defineConfig({ library: { type: 'window', name: moduleFederationName }, }, publicPath: 'http://127.0.0.1:9000/', + hash: false, }); diff --git a/examples/qiankun-master/.umirc.ts b/examples/qiankun-master/.umirc.ts index 03d00827e65d..5f1cb448cae2 100644 --- a/examples/qiankun-master/.umirc.ts +++ b/examples/qiankun-master/.umirc.ts @@ -10,6 +10,10 @@ export default { name: 'slave-app2', entry: 'http://127.0.0.1:7002', // your slave app address }, + { + name: 'slave-mf', + entry: 'http://127.0.0.1:8000', // your slave app address + }, ], }, slave: {}, @@ -22,6 +26,10 @@ export default { path: '/slave/*', microApp: 'slave', }, + { + path: '/slave-mf/*', + microApp: 'slave-mf', + }, { path: '/animal', routes: [ diff --git a/examples/qiankun-master/package.json b/examples/qiankun-master/package.json index 24cc8ef3152d..c589df08446f 100644 --- a/examples/qiankun-master/package.json +++ b/examples/qiankun-master/package.json @@ -2,8 +2,8 @@ "name": "@example/qiankun-master", "private": true, "scripts": { - "dev": "cross-env PORT=8888 max dev", - "preview": "max preview --port 8888", + "dev": "cross-env PORT=8889 max dev", + "preview": "max preview --port 8889", "setup": "max setup", "start": "npm run dev" }, diff --git a/examples/qiankun-slave-app2/.umirc.ts b/examples/qiankun-slave-app2/.umirc.ts index 008c83a013aa..59791471ce9e 100644 --- a/examples/qiankun-slave-app2/.umirc.ts +++ b/examples/qiankun-slave-app2/.umirc.ts @@ -9,4 +9,5 @@ export default { { path: '/hello', component: 'hello' }, ], runtimePublicPath: {}, + hash: false, }; diff --git a/examples/qiankun-slave-app2/package.json b/examples/qiankun-slave-app2/package.json index 36ed68092086..3ab4c0263798 100644 --- a/examples/qiankun-slave-app2/package.json +++ b/examples/qiankun-slave-app2/package.json @@ -2,7 +2,9 @@ "name": "@example/qiankun-slave-app2", "private": true, "scripts": { + "build": "max build", "dev": "max dev", + "preview": "max preview --port 7002", "setup": "max setup", "start": "npm run dev" }, diff --git a/examples/qiankun-slave/.umirc.ts b/examples/qiankun-slave/.umirc.ts index dd4c21685643..20756a662717 100644 --- a/examples/qiankun-slave/.umirc.ts +++ b/examples/qiankun-slave/.umirc.ts @@ -9,4 +9,5 @@ export default { { path: '/count', component: 'count' }, { path: '/nav', component: 'nav' }, ], + hash: false, }; diff --git a/examples/qiankun-slave/cypress/e2e/example.cy.ts b/examples/qiankun-slave/cypress/e2e/example.cy.ts index 96ad7d86cfb4..8a5895f08e01 100644 --- a/examples/qiankun-slave/cypress/e2e/example.cy.ts +++ b/examples/qiankun-slave/cypress/e2e/example.cy.ts @@ -6,7 +6,7 @@ describe('QianKun Plugin', () => { it('can navigate to slave', () => { // contains https://docs.cypress.io/api/commands/contains cy.visit('/home'); - cy.get('button').click(); + cy.get('a').click(); cy.contains('Slave Home Page'); }); @@ -20,6 +20,15 @@ describe('QianKun Plugin', () => { cy.contains('count:1'); }); + it('support app using module-federation', () => { + cy.visit('/slave-mf/dynamic-import'); + + cy.contains('remote Counter'); + cy.contains('remote hooks counter10'); + cy.get('[data-testid="remote-button"]').click(); + cy.contains('remote hooks counter11'); + }); + describe('manual loaded app', function () { it('be loaded', () => { cy.visit('/manual-slave/home'); diff --git a/examples/qiankun-slave/package.json b/examples/qiankun-slave/package.json index 8d71b231533a..4694a8cfc03b 100644 --- a/examples/qiankun-slave/package.json +++ b/examples/qiankun-slave/package.json @@ -3,11 +3,16 @@ "private": true, "scripts": { "dev": "cross-env PORT=5555 max dev", - "e2e:ci": "cross-env-shell PORT=8888 start-test start:all-qiankun 8888 test:ci", - "e2e:local": "cross-env-shell PORT=8888 start-test start:all-qiankun 8888 test:local", + "e2e:ci": "npm run e2e:ci:dev", + "e2e:ci:dev": "cross-env PORT=8889 start-test start:all-qiankun:dev 8889 test:ci", + "e2e:ci:preview": "cross-env PORT=8889 start-test start:all-qiankun:preview 8889 test:ci:preview", + "e2e:local": "cross-env PORT=8889 start-test start:all-qiankun:dev 8889 test:local", + "e2e:local:preview": "cross-env-shell PORT=8889 start-test start:all-qiankun:preview 8889 test:local", + "preview": "cross-env PORT=5555 max preview --port 5555", "setup": "max setup", "start": "npm run dev", - "start:all-qiankun": "pnpm umi-scripts turbo --filter @example/qiankun-* dev", + "start:all-qiankun:dev": "pnpm umi-scripts turbo --filter @example/qiankun-* --filter @example/mf-* dev", + "start:all-qiankun:preview": "pnpm umi-scripts turbo --filter @example/qiankun-* --filter @example/mf-* preview", "test:ci": "pnpm umi-scripts cypress", "test:local": "cypress open" }, diff --git a/packages/plugins/src/mf.test.ts b/packages/plugins/src/mf.test.ts index 4ccc081e370c..c5a25af8248f 100644 --- a/packages/plugins/src/mf.test.ts +++ b/packages/plugins/src/mf.test.ts @@ -117,6 +117,7 @@ async function executePlugin(mfConfig: any) { modifyWebpackConfig(fn: Function) { modifier = fn; }, + register() {}, addRuntimePluginKey() {}, modifyDefaultConfig() {}, }; diff --git a/packages/plugins/src/mf.ts b/packages/plugins/src/mf.ts index f5d86a3d1b7d..3ed61c9325ec 100644 --- a/packages/plugins/src/mf.ts +++ b/packages/plugins/src/mf.ts @@ -1,4 +1,5 @@ -import { existsSync, opendirSync } from 'fs'; +import { parseModule } from '@umijs/bundler-utils'; +import { existsSync, opendirSync, readFileSync } from 'fs'; import { join } from 'path'; import type { IApi } from 'umi'; import { lodash, winPath } from 'umi/plugin-utils'; @@ -52,7 +53,7 @@ export default function mf(api: IApi) { } if (!isEmpty(remotes)) { - if (!api.config.mfsu) { + if (api.env === 'production' || !api.config.mfsu) { changeUmiEntry(config); } } @@ -126,16 +127,54 @@ export default function mf(api: IApi) { }, tplPath: winPath(join(MF_TEMPLATES_DIR, 'runtime.ts.tpl')), }); + }); - if (api.env === 'development' && api.config.mfsu) { - // skip mfsu already dynamic import - return; - } + api.register({ + key: 'onGenerateFiles', + // ensure after generate tpm files + stage: 10001, + fn: async () => { + if (api.env === 'development' && api.config.mfsu) { + // skip mfsu already dynamic import + return; + } - api.writeTmpFile({ - content: `import('${winPath(join(api.paths.absTmpPath, 'umi.ts'))}')`, - path: mfAsyncEntryFileName, - }); + const entry = join(api.paths.absTmpPath, 'umi.ts'); + const content = readFileSync( + join(api.paths.absTmpPath, 'umi.ts'), + 'utf-8', + ); + + const [_imports, exports] = await parseModule({ content, path: entry }); + + const mfEntryContent: string[] = []; + let hasDefaultExport = false; + if (exports.length) { + mfEntryContent.push( + `const umiExports = await import('${winPath(entry)}')`, + ); + for (const exportName of exports) { + if (exportName === 'default') { + hasDefaultExport = true; + mfEntryContent.push(`export default umiExports.${exportName}`); + } else { + mfEntryContent.push( + `export const ${exportName} = umiExports.${exportName}`, + ); + } + } + } else { + mfEntryContent.push(`import('${winPath(entry)}')`); + } + if (!hasDefaultExport) { + mfEntryContent.push('export default 1'); + } + + api.writeTmpFile({ + content: mfEntryContent.join('\n'), + path: mfAsyncEntryFileName, + }); + }, }); function formatRemotes() {