Skip to content

Commit

Permalink
feat: prevent css flickering (#2502)
Browse files Browse the repository at this point in the history
  • Loading branch information
2heal1 authored May 17, 2024
1 parent 7f0efb3 commit 94491f6
Show file tree
Hide file tree
Showing 26 changed files with 430 additions and 178 deletions.
12 changes: 12 additions & 0 deletions .changeset/long-spies-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@module-federation/webpack-bundler-runtime': patch
'@module-federation/devtools': patch
'@module-federation/enhanced': patch
'@module-federation/manifest': patch
'@module-federation/modern-js': patch
'@module-federation/runtime': patch
'@module-federation/modernjs': patch
'@module-federation/sdk': patch
---

feat: add MFComponent
46 changes: 25 additions & 21 deletions apps/modernjs-ssr/dynamic-nested-remote/src/components/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
import React from 'react';
import Button from 'antd/lib/button';
import { loadRemote, registerRemotes } from '@modern-js/runtime/mf';
import {
loadRemote,
registerRemotes,
MFReactComponent,
} from '@modern-js/runtime/mf';
import stuff from './stuff.module.css';

const isServer = typeof window === 'undefined';
registerRemotes([
{
name: 'dynamic_remote',
entry: 'http://localhost:3008/mf-manifest.json',
},
]);

const Comp = React.lazy(() =>
loadRemote('dynamic_remote/Image').then((m) => {
return {
default: () => (
<div>
<link
href="http://localhost:3008/static/css/async/__federation_expose_Image.css"
rel="stylesheet"
type="text/css"
/>
<span>11</span>
<m.default />
</div>
),
};
}),
);
// const Comp = React.lazy(() =>
// loadRemote('dynamic_remote/Image').then((m) => {
// return {
// default: () => (
// <div>
// <link
// href="http://localhost:3008/static/css/async/__federation_expose_Image.css"
// rel="stylesheet"
// type="text/css"
// />
// <span>11</span>
// <m.default />
// </div>
// ),
// };
// }),
// );

const LazyButton = React.lazy(() =>
import('./Button').then((m) => {
Expand Down Expand Up @@ -57,9 +60,10 @@ export default (): JSX.Element => (
Click me to test <strong>dynamic nested remote</strong> interactive!
</Button>

<React.Suspense fallback="loading">
{/* <React.Suspense fallback="loading">
<Comp />
</React.Suspense>
</React.Suspense> */}
<MFReactComponent id="dynamic_remote/Image" />
<React.Suspense fallback="loading btn">
<LazyButton />
</React.Suspense>
Expand Down
3 changes: 2 additions & 1 deletion apps/modernjs-ssr/nested-remote/src/components/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { MFReactComponent, collectLinks } from '@modern-js/runtime/mf';
import Comp from 'remote/Image';
import Button from 'antd/lib/button';
import stuff from './stuff.module.css';
Expand All @@ -14,7 +15,7 @@ export default (): JSX.Element => (
>
Click me to test <strong>nested remote</strong> interactive!
</Button>

{collectLinks('remote/Image')}
<Comp />
</div>
);
1 change: 1 addition & 0 deletions apps/modernjs-ssr/nested-remote/src/modern-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// <reference types='@modern-js/app-tools/types' />
/// <reference types='@modern-js/runtime/types' />
/// <reference types='@modern-js/runtime/types/router' />
/// <reference types='@module-federation/modern-js/types' />
13 changes: 5 additions & 8 deletions apps/modernjs/modern.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { appTools, defineConfig } from '@modern-js/app-tools';
import {
ModuleFederationPlugin,
AsyncBoundaryPlugin,
} from '@module-federation/enhanced';
import { ModuleFederationPlugin } from '@module-federation/enhanced';
// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
dev: {
Expand Down Expand Up @@ -36,12 +33,12 @@ export default defineConfig({
}

appendPlugins([
new AsyncBoundaryPlugin({
excludeChunk: chunk => chunk.name === 'app1',
eager: module => /\.federation/.test(module?.request || ''),
}),
new ModuleFederationPlugin({
name: 'app1',
async: {
excludeChunk: chunk => chunk.name === 'app1',
eager: module => /\.federation/.test(module?.request || ''),
},
exposes: {
'./thing': './src/test.ts',
},
Expand Down
9 changes: 2 additions & 7 deletions packages/dts-plugin/src/core/lib/DTSManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Manifest,
inferAutoPublicPath,
} from '@module-federation/sdk';
import cloneDeepWith from 'lodash.clonedeepwith';

import { retrieveRemoteConfig } from '../configurations/remotePlugin';
import { createTypesArchive, downloadTypesArchive } from './archiveHandler';
Expand All @@ -27,6 +26,7 @@ import {
} from '../constant';
import axios from 'axios';
import { fileLog } from '../../server';
import { cloneDeepOptions } from './utils';

export const MODULE_DTS_MANAGER_IDENTIFIER = 'MF DTS Manager';

Expand All @@ -44,12 +44,7 @@ class DTSManager {
extraOptions: Record<string, any>;

constructor(options: DTSManagerOptions) {
this.options = cloneDeepWith(options, (_value, key) => {
// moduleFederationConfig.manifest may have un serialization options
if (key === 'manifest') {
return false;
}
});
this.options = cloneDeepOptions(options);
this.runtimePkgs = [
'@module-federation/runtime',
'@module-federation/enhanced/runtime',
Expand Down
10 changes: 3 additions & 7 deletions packages/dts-plugin/src/core/lib/DtsWorker.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import path from 'path';
import cloneDeepWith from 'lodash.clonedeepwith';

import { type RpcWorker, createRpcWorker } from '../rpc/index';
import type { RpcMethod } from '../rpc/types';
import type { DTSManagerOptions } from '../interfaces/DTSManagerOptions';
import type { DTSManager } from './DTSManager';
import { cloneDeepOptions } from './utils';

export type DtsWorkerOptions = DTSManagerOptions;

Expand All @@ -14,12 +14,8 @@ export class DtsWorker {
private _res: Promise<any>;

constructor(options: DtsWorkerOptions) {
this._options = cloneDeepWith(options, (_value, key) => {
// moduleFederationConfig.manifest may have un serialization options
if (key === 'manifest') {
return false;
}
});
this._options = cloneDeepOptions(options);

this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(
path.resolve(__dirname, './forkGenerateDts.js'),
Expand Down
13 changes: 12 additions & 1 deletion packages/dts-plugin/src/core/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
} from './typeScriptCompiler';
import { moduleFederationPlugin } from '@module-federation/sdk';
import ansiColors from 'ansi-colors';
import tsconfigPaths from 'vite-tsconfig-paths';
import cloneDeepWith from 'lodash.clonedeepwith';
import { DTSManagerOptions } from '../interfaces/DTSManagerOptions';

export function getDTSManagerConstructor(
implementation?: string,
Expand Down Expand Up @@ -93,3 +94,13 @@ export const isTSProject = (
return false;
}
};

export function cloneDeepOptions(options: DTSManagerOptions) {
const excludeKeys = ['manifest', 'async'];
return cloneDeepWith(options, (_value, key) => {
// moduleFederationConfig.manifest may have un serialization options
if (typeof key === 'string' && excludeKeys.includes(key)) {
return false;
}
});
}
10 changes: 3 additions & 7 deletions packages/dts-plugin/src/dev-worker/DevWorker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import cloneDeepWith from 'lodash.clonedeepwith';
import { DTSManagerOptions, rpc } from '../core/index';
import { cloneDeepOptions } from '../core/lib/utils';

export interface DevWorkerOptions extends DTSManagerOptions {
name: string;
Expand All @@ -14,12 +14,8 @@ export class DevWorker {
private _res: Promise<any>;

constructor(options: DevWorkerOptions) {
this._options = cloneDeepWith(options, (_value, key): void | any => {
// moduleFederationConfig.manifest may have un serialization options
if (key === 'manifest') {
return false;
}
});
this._options = cloneDeepOptions(options);

this.removeUnSerializationOptions();
this._rpcWorker = rpc.createRpcWorker(
path.resolve(__dirname, './forkDevWorker.js'),
Expand Down
7 changes: 2 additions & 5 deletions packages/enhanced/src/lib/container/AsyncBoundaryPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { normalizeWebpackPath } from '@module-federation/sdk/normalize-webpack-path';
import { moduleFederationPlugin } from '@module-federation/sdk';
import type {
Compiler,
Compilation,
Expand Down Expand Up @@ -27,11 +28,7 @@ type InferStartupRenderContext<T> = T extends SyncWaterfallHook<

type StartupRenderContext = InferStartupRenderContext<RenderStartup>;

export interface Options {
eager?: RegExp | ((module: Module) => boolean);
excludeChunk?: (chunk: Chunk) => boolean;
}

export type Options = moduleFederationPlugin.AsyncBoundaryOptions;
class AsyncEntryStartupPlugin {
private _options: Options;
private _runtimeChunks = new Map<string | number, Chunk | undefined>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,30 @@ export default {
},
},
},
AsyncBoundaryOptions: {
description: 'Make entrypoints startup as async chunks',
type: 'object',
properties: {
eager: {
description: 'whether eager',
anyOf: [
{
instanceof: 'RegExp',
tsType: 'RegExp',
tsType: '((module: any) => boolean)',
},
{
type: 'boolean',
},
],
},
excludeChunk: {
description: 'exclude chunk',
instanceof: 'Function',
tsType: '(chunk: any) => boolean',
},
},
},
},
title: 'ModuleFederationPluginOptions',
type: 'object',
Expand Down Expand Up @@ -756,7 +780,14 @@ export default {
},
async: {
description: 'Make entrypoints startup as async chunks',
type: 'boolean',
anyOf: [
{
$ref: '#/definitions/AsyncBoundaryOptions',
},
{
type: 'boolean',
},
],
},
},
};
45 changes: 21 additions & 24 deletions packages/manifest/src/ManifestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,47 +59,44 @@ class ManifestManager {
...stats,
};

manifest.exposes = Object.keys(stats.exposes).reduce((sum, cur) => {
const statsExpose = manifest.exposes[cur] as StatsExpose;
manifest.exposes = stats.exposes.reduce((sum, cur) => {
const expose: ManifestExpose = {
id: statsExpose.id,
name: statsExpose.name,
assets: statsExpose.assets,
path: statsExpose.path,
id: cur.id,
name: cur.name,
assets: cur.assets,
path: cur.path,
};
sum.push(expose);
return sum;
}, [] as ManifestExpose[]);
manifest.shared = Object.keys(stats.shared).reduce((sum, cur) => {
const statsShared = manifest.shared[cur] as StatsShared;
manifest.shared = stats.shared.reduce((sum, cur) => {
const shared: ManifestShared = {
id: statsShared.id,
name: statsShared.name,
version: statsShared.version,
singleton: statsShared.singleton,
requiredVersion: statsShared.requiredVersion,
hash: statsShared.hash,
assets: statsShared.assets,
id: cur.id,
name: cur.name,
version: cur.version,
singleton: cur.singleton,
requiredVersion: cur.requiredVersion,
hash: cur.hash,
assets: cur.assets,
};
sum.push(shared);
return sum;
}, [] as ManifestShared[]);

manifest.remotes = Object.keys(stats.remotes).reduce((sum, cur) => {
const statsRemote = manifest.remotes[cur] as StatsRemote;
manifest.remotes = stats.remotes.reduce((sum, cur) => {
// @ts-ignore version/entry will be added as follow
const remote: ManifestRemote = {
federationContainerName: statsRemote.federationContainerName,
moduleName: statsRemote.moduleName,
alias: statsRemote.alias,
federationContainerName: cur.federationContainerName,
moduleName: cur.moduleName,
alias: cur.alias,
};

if ('entry' in statsRemote) {
if ('entry' in cur) {
// @ts-ignore
remote.entry = statsRemote.entry;
} else if ('version' in statsRemote) {
remote.entry = cur.entry;
} else if ('version' in cur) {
// @ts-ignore
remote.entry = statsRemote.version;
remote.entry = cur.version;
}

sum.push(remote);
Expand Down
8 changes: 6 additions & 2 deletions packages/manifest/src/StatsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@module-federation/sdk';
import { Compilation, Compiler, StatsCompilation, StatsModule } from 'webpack';
import {
isDev,
getAssetsByChunk,
findChunk,
getAssetsByChunkIDs,
Expand Down Expand Up @@ -400,8 +401,11 @@ class StatsManager {
try {
const { disableEmit } = extraOptions;
const existedStats = compilation.getAsset(this.fileName);
if (existedStats) {
return JSON.parse(existedStats.source.source().toString());
if (existedStats && !isDev()) {
return {
stats: JSON.parse(existedStats.source.source().toString()),
filename: this.fileName,
};
}
const { manifest: manifestOptions = {} } = this._options;
let stats = await this._generateStats(compiler, compilation);
Expand Down
Loading

0 comments on commit 94491f6

Please sign in to comment.