Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: perf: reduce chunk map if remote entry chunk #2911

Open
wants to merge 20 commits into
base: chore/split-chunks-demo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
71fa675
perf: reduce chunk map if remote entry chunk
ScriptedAlchemy Aug 31, 2024
b314d42
perf: reduce chunk map if remote entry chunk
ScriptedAlchemy Aug 31, 2024
7c60d1f
perf: reduce chunk map if remote entry chunk
ScriptedAlchemy Aug 31, 2024
93d6502
chore: use non vendored runtime to test
ScriptedAlchemy Aug 31, 2024
56bd582
chore: use non vendored runtime to test
ScriptedAlchemy Aug 31, 2024
472288f
fix: use normal module replacement for bundler runtime import
ScriptedAlchemy Aug 31, 2024
23e818a
chore: use esm for runtime resolve paths
ScriptedAlchemy Aug 31, 2024
f9e146d
chore: use esm for runtime resolve paths
ScriptedAlchemy Aug 31, 2024
ed8b69a
chore: use esm for runtime resolve paths
ScriptedAlchemy Aug 31, 2024
fbb7f1e
chore: use esm for runtime resolve paths
ScriptedAlchemy Aug 31, 2024
828daa2
chore: use esm for runtime resolve paths
ScriptedAlchemy Aug 31, 2024
e823904
fix: set used exports for all
ScriptedAlchemy Sep 1, 2024
9a9f707
fix: set used exports for all
ScriptedAlchemy Sep 2, 2024
09129c1
fix: set used exports for all
ScriptedAlchemy Sep 2, 2024
e64f7ed
fix: set used exports for all
ScriptedAlchemy Sep 2, 2024
98afaff
Merge branch 'chore/split-chunks-demo' into light-refer-chunk-remote
ScriptedAlchemy Sep 2, 2024
bbe7520
fix(enhanced): do not use embedded bundler runtime
ScriptedAlchemy Sep 2, 2024
258878d
Merge branch 'chore/split-chunks-demo' into light-refer-chunk-remote
ScriptedAlchemy Sep 4, 2024
23a362f
Merge branch 'chore/split-chunks-demo' into light-refer-chunk-remote
ScriptedAlchemy Sep 4, 2024
235f9cc
chore: docs
ScriptedAlchemy Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions apps/3000-home/cypress/e2e/app.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ describe('3000-home/', () => {
it('should display welcome message', () => {
getH1().contains('This is SPA combined');
});
it('Api endpoint works', () => {
const urls = ['/api/test'];
urls.forEach((url) => {
cy.request(url); // This makes a GET request, not a full page visit
});

describe('API endpoint should return json', () => {
it('Query Endpoint', () => {
cy.request('/api/test').then((response) => {
expect(response.headers['content-type']).to.include('application/json');
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const ConcatenatedModule = require(
) as typeof import('webpack/lib/optimize/ConcatenatedModule');

const PLUGIN_NAME = 'HoistContainerReferences';

/**
* This class is used to hoist container references in the code.
* @constructor
Expand Down Expand Up @@ -68,7 +69,11 @@ export class HoistContainerReferences implements WebpackPluginInstance {

// Hook into the optimizeDependencies phase
compilation.hooks.optimizeDependencies.tap(
PLUGIN_NAME,
{
name: PLUGIN_NAME,
// basic optimization stage - it runs first
stage: -10,
},
(modules: Iterable<Module>) => {
if (this.entryFilePath) {
let runtime: RuntimeSpec | undefined;
Expand All @@ -87,15 +92,24 @@ export class HoistContainerReferences implements WebpackPluginInstance {
module instanceof NormalModule &&
module.resource === this.bundlerRuntimeDep
) {
const exportsInfo: ExportsInfo =
moduleGraph.getExportsInfo(module);
//Since i dont use the import federation var, tree shake will eliminate it.
exportsInfo.setUsedInUnknownWay(runtime);
moduleGraph.addExtraReason(module, this.explanation);
if (module.factoryMeta === undefined) {
module.factoryMeta = {};
const allRefs = this.getAllReferencedModules(
compilation,
module,
'initial',
);
for (const module of allRefs) {
const exportsInfo: ExportsInfo =
moduleGraph.getExportsInfo(module);
// Since i dont use the import federation var, tree shake will eliminate it.
// also because currently the runtime is copied into all runtime chunks
// some might not have the runtime import in the tree to begin with
exportsInfo.setUsedInUnknownWay(runtime);
moduleGraph.addExtraReason(module, this.explanation);
if (module.factoryMeta === undefined) {
module.factoryMeta = {};
}
module.factoryMeta.sideEffectFree = false;
}
module.factoryMeta.sideEffectFree = false;
}
}
}
Expand Down
21 changes: 16 additions & 5 deletions packages/enhanced/src/lib/container/RemoteRuntimeModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type ExternalModule from 'webpack/lib/ExternalModule';
import type FallbackModule from './FallbackModule';
import type { RemotesOptions } from '@module-federation/webpack-bundler-runtime';
import { FEDERATION_SUPPORTED_TYPES } from '@module-federation/webpack-bundler-runtime/constant';
import ContainerEntryModule from './ContainerEntryModule';

const extractUrlAndGlobal = require(
normalizeWebpackPath('webpack/lib/util/extractUrlAndGlobal'),
Expand Down Expand Up @@ -44,12 +45,22 @@ class RemoteRuntimeModule extends RuntimeModule {
// chunkReferences = this.chunk.getAllAsyncChunks();
// }
// }
let chunkReferences;
if (this.chunkGraph && this.chunk) {
const entryMods = Array.from(
this.chunkGraph.getChunkEntryModulesIterable(this.chunk),
);
const isRemoteEntry = entryMods.some(
(m) => m instanceof ContainerEntryModule,
);
chunkReferences = isRemoteEntry
? this.chunk.getAllAsyncChunks()
: this.chunk.getAllReferencedChunks();
} else {
chunkReferences = this.chunk?.getAllReferencedChunks() || [];
}

const allChunks = [
...Array.from(this.chunk?.getAllReferencedChunks() || []),
];

for (const chunk of allChunks) {
for (const chunk of chunkReferences) {
const modules = chunkGraph?.getChunkModulesIterableBySourceType(
chunk,
'remote',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const BundlerRuntimePath = require.resolve(
);

const VendoredBundlerRuntimePath = require.resolve(
'@module-federation/webpack-bundler-runtime/vendor',
'@module-federation/webpack-bundler-runtime',
{
paths: [RuntimeToolsPath],
},
Expand Down Expand Up @@ -119,7 +119,7 @@ class FederationRuntimePlugin {
return Template.asString([
`import federation from '${normalizedBundlerRuntimePath}';`,
runtimePluginTemplates,
embedRuntime ? '' : embedRuntimeLines,
embedRuntimeLines,
`if(!${federationGlobal}.instance){`,
Template.indent([
runtimePluginNames.length
Expand Down Expand Up @@ -325,6 +325,7 @@ class FederationRuntimePlugin {
};

if (this.options?.embedRuntime) {
runtimePath = runtimePath.replace('.cjs', '.esm');
// should use normal module replacement instead?
if (!compiler.options.resolve.alias['@module-federation/runtime$']) {
compiler.options.resolve.alias['@module-federation/runtime$'] =
Expand Down Expand Up @@ -389,7 +390,7 @@ class FederationRuntimePlugin {

if (this.options?.implementation) {
const runtimePath = this.options.embedRuntime
? '@module-federation/webpack-bundler-runtime/vendor'
? '@module-federation/webpack-bundler-runtime'
: '@module-federation/webpack-bundler-runtime';
this.bundlerRuntimePath = require.resolve(runtimePath, {
paths: [this.options.implementation],
Expand All @@ -409,6 +410,19 @@ class FederationRuntimePlugin {
this.getFilePath(),
this.bundlerRuntimePath,
).apply(compiler);

new compiler.webpack.NormalModuleReplacementPlugin(
/@module-federation\/runtime/,
(resolveData) => {
if (/webpack-bundler-runtime/.test(resolveData.contextInfo.issuer)) {
resolveData.request = RuntimePath.replace('cjs', 'esm');

if (resolveData.createData) {
resolveData.createData.request = resolveData.request;
}
}
},
).apply(compiler);
}
this.prependEntry(compiler);
this.injectRuntime(compiler);
Expand Down
27 changes: 17 additions & 10 deletions packages/enhanced/src/lib/sharing/ConsumeSharedRuntimeModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { normalizeWebpackPath } from '@module-federation/sdk/normalize-webpack-p
import type { Module, ChunkGraph, Compilation, Chunk } from 'webpack';
import ConsumeSharedModule from './ConsumeSharedModule';
import { getFederationGlobalScope } from '../container/runtime/utils';
import ContainerEntryModule from '../container/ContainerEntryModule';

const { Template, RuntimeGlobals, RuntimeModule } = require(
normalizeWebpackPath('webpack'),
Expand Down Expand Up @@ -92,15 +93,21 @@ class ConsumeSharedRuntimeModule extends RuntimeModule {
moduleIdToSourceMapping.set(id, sharedInfoAndHandlerStr);
}
};
// const chunkReferences = this._runtimeRequirements.has(
// 'federation-entry-startup',
// )
// ? this.chunk?.getAllReferencedChunks()
// : this.chunk?.getAllAsyncChunks();
//
// const allChunks = chunkReferences || [];
const allChunks = [...(this.chunk?.getAllReferencedChunks() || [])];
for (const chunk of allChunks) {
let chunkReferences;
if (this.chunkGraph && this.chunk) {
const entryMods = Array.from(
this.chunkGraph.getChunkEntryModulesIterable(this.chunk),
);
const isRemoteEntry = entryMods.some(
(m) => m instanceof ContainerEntryModule,
);
chunkReferences = isRemoteEntry
? this.chunk.getAllAsyncChunks()
: this.chunk.getAllReferencedChunks();
} else {
chunkReferences = this.chunk?.getAllReferencedChunks() || [];
}
for (const chunk of chunkReferences) {
const modules = chunkGraph.getChunkModulesIterableBySourceType(
chunk,
'consume-shared',
Expand All @@ -116,7 +123,7 @@ class ConsumeSharedRuntimeModule extends RuntimeModule {
(chunkToModuleMapping[chunk.id.toString()] = []),
);
}
for (const chunk of [...(this.chunk?.getAllInitialChunks() || [])]) {
for (const chunk of this.chunk?.getAllInitialChunks() || []) {
const modules = chunkGraph.getChunkModulesIterableBySourceType(
chunk,
'consume-shared',
Expand Down
19 changes: 1 addition & 18 deletions webpack/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import Compilation from './lib/Compilation';
import Stats from './lib/Stats';
import NormalModuleReplacementPlugin from './lib/NormalModuleReplacementPlugin';
import MultiCompiler from './lib/MultiCompiler';
import Parser from './lib/Parser';
import ModuleDependency from './lib/dependencies/ModuleDependency';
Expand Down Expand Up @@ -3178,24 +3179,6 @@ declare interface NormalModuleLoaderContext<OptionsType> {
_compiler?: Compiler;
}

declare class NormalModuleReplacementPlugin {
/**
* Create an instance of the plugin
*/
constructor(
resourceRegExp: RegExp,
newResource: string | ((arg0: ResolveData) => void),
);

resourceRegExp: RegExp;
newResource: string | ((arg0: ResolveData) => void);

/**
* Apply the plugin
*/
apply(compiler: Compiler): void;
}

declare class NullDependency extends Dependency {
constructor();

Expand Down
Loading