@module-federation/node 0.0.0-feat-node-support-1702930274547 → 0.0.0-next-20231219093413
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/.babelrc +10 -0
- package/.eslintrc.json +47 -0
- package/CHANGELOG.md +1352 -0
- package/LICENSE +21 -0
- package/README.md +192 -0
- package/assets/banner.png +0 -0
- package/dist/jest.config.js +19 -0
- package/dist/jest.config.js.map +1 -0
- package/dist/package.json +62 -0
- package/dist/src/filesystem/stratagies.d.ts +11 -0
- package/{src → dist/src}/filesystem/stratagies.js +11 -31
- package/dist/src/filesystem/stratagies.js.map +1 -0
- package/{src → dist/src}/index.d.ts +2 -0
- package/{src → dist/src}/index.js +5 -1
- package/dist/src/index.js.map +1 -0
- package/dist/src/plugins/AutomaticPublicPathPlugin.js.map +1 -0
- package/dist/src/plugins/ChunkCorrelationPlugin.js +442 -0
- package/dist/src/plugins/ChunkCorrelationPlugin.js.map +1 -0
- package/dist/src/plugins/CommonJsChunkLoadingPlugin.js.map +1 -0
- package/{src → dist/src}/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js +13 -0
- package/dist/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js.map +1 -0
- package/dist/src/plugins/NodeContainerPlugin.d.ts +57 -0
- package/dist/src/plugins/NodeContainerPlugin.js +105 -0
- package/dist/src/plugins/NodeContainerPlugin.js.map +1 -0
- package/{src → dist/src}/plugins/NodeFederationPlugin.d.ts +20 -0
- package/dist/src/plugins/NodeFederationPlugin.js +127 -0
- package/dist/src/plugins/NodeFederationPlugin.js.map +1 -0
- package/dist/src/plugins/RemotePublicPathRuntimeModule.js.map +1 -0
- package/{src → dist/src}/plugins/StreamingTargetPlugin.js.map +1 -1
- package/{src → dist/src}/plugins/UniversalFederationPlugin.d.ts +0 -1
- package/{src → dist/src}/plugins/UniversalFederationPlugin.js +3 -3
- package/dist/src/plugins/UniversalFederationPlugin.js.map +1 -0
- package/{src → dist/src}/plugins/webpackChunkUtilities.js +5 -1
- package/dist/src/plugins/webpackChunkUtilities.js.map +1 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/flush-chunks.js.map +1 -0
- package/dist/src/utils/hot-reload.js.map +1 -0
- package/dist/src/utils/index.js.map +1 -0
- package/global.d.ts +27 -0
- package/jest.config.js +14 -16
- package/package.json +17 -23
- package/project.json +58 -0
- package/src/filesystem/stratagies.ts +131 -0
- package/src/index.ts +7 -0
- package/src/plugins/AutomaticPublicPathPlugin.ts +48 -0
- package/src/plugins/ChunkCorrelationPlugin.js +474 -307
- package/src/plugins/CommonJsChunkLoadingPlugin.ts +168 -0
- package/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.ts +239 -0
- package/src/plugins/NodeContainerPlugin.ts +150 -0
- package/src/plugins/NodeFederationPlugin.ts +185 -0
- package/src/plugins/RemotePublicPathRuntimeModule.ts +137 -0
- package/src/plugins/StreamingTargetPlugin.md +88 -0
- package/src/plugins/StreamingTargetPlugin.ts +81 -0
- package/src/plugins/UniversalFederationPlugin.ts +81 -0
- package/src/plugins/webpackChunkUtilities.ts +383 -0
- package/src/types/index.ts +7 -0
- package/src/utils/flush-chunks.ts +182 -0
- package/src/utils/hot-reload.ts +169 -0
- package/src/utils/index.ts +2 -0
- package/tsconfig.json +28 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.spec.json +9 -0
- package/jest.config.js.map +0 -1
- package/src/filesystem/stratagies.d.ts +0 -18
- package/src/filesystem/stratagies.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/plugins/AutomaticPublicPathPlugin.js.map +0 -1
- package/src/plugins/ChunkCorrelationPlugin.js.map +0 -1
- package/src/plugins/CommonJsChunkLoadingPlugin.js.map +0 -1
- package/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js.map +0 -1
- package/src/plugins/NodeFederationPlugin.js +0 -76
- package/src/plugins/NodeFederationPlugin.js.map +0 -1
- package/src/plugins/RemotePublicPathRuntimeModule.js.map +0 -1
- package/src/plugins/UniversalFederationPlugin.js.map +0 -1
- package/src/plugins/webpackChunkUtilities.js.map +0 -1
- package/src/types/index.js.map +0 -1
- package/src/utils/flush-chunks.js.map +0 -1
- package/src/utils/hot-reload.js.map +0 -1
- package/src/utils/index.js.map +0 -1
- package/{jest.config.d.ts → dist/jest.config.d.ts} +0 -0
- package/{src → dist/src}/plugins/AutomaticPublicPathPlugin.d.ts +0 -0
- package/{src → dist/src}/plugins/AutomaticPublicPathPlugin.js +0 -0
- package/{src → dist/src}/plugins/ChunkCorrelationPlugin.d.ts +6 -6
- /package/{src → dist/src}/plugins/CommonJsChunkLoadingPlugin.d.ts +0 -0
- /package/{src → dist/src}/plugins/CommonJsChunkLoadingPlugin.js +0 -0
- /package/{src → dist/src}/plugins/DynamicFilesystemChunkLoadingRuntimeModule.d.ts +0 -0
- /package/{src → dist/src}/plugins/RemotePublicPathRuntimeModule.d.ts +0 -0
- /package/{src → dist/src}/plugins/RemotePublicPathRuntimeModule.js +0 -0
- /package/{src → dist/src}/plugins/StreamingTargetPlugin.d.ts +0 -0
- /package/{src → dist/src}/plugins/StreamingTargetPlugin.js +0 -0
- /package/{src → dist/src}/plugins/webpackChunkUtilities.d.ts +0 -0
- /package/{src → dist/src}/types/index.d.ts +0 -0
- /package/{src → dist/src}/types/index.js +0 -0
- /package/{src → dist/src}/utils/flush-chunks.d.ts +0 -0
- /package/{src → dist/src}/utils/flush-chunks.js +0 -0
- /package/{src → dist/src}/utils/hot-reload.d.ts +0 -0
- /package/{src → dist/src}/utils/hot-reload.js +0 -0
- /package/{src → dist/src}/utils/index.d.ts +0 -0
- /package/{src → dist/src}/utils/index.js +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { Chunk, Compiler, Compilation, ChunkGraph } from 'webpack';
|
|
2
|
+
import { normalizeWebpackPath } from '@module-federation/sdk/normalize-webpack-path';
|
|
3
|
+
import type { ModuleFederationPluginOptions } from '../types';
|
|
4
|
+
const StartupChunkDependenciesPlugin = require(
|
|
5
|
+
normalizeWebpackPath('webpack/lib/runtime/StartupChunkDependenciesPlugin'),
|
|
6
|
+
) as typeof import('webpack/lib/runtime/StartupChunkDependenciesPlugin');
|
|
7
|
+
import ChunkLoadingRuntimeModule from './DynamicFilesystemChunkLoadingRuntimeModule';
|
|
8
|
+
import AutoPublicPathRuntimeModule from './RemotePublicPathRuntimeModule';
|
|
9
|
+
|
|
10
|
+
interface DynamicFilesystemChunkLoadingOptions
|
|
11
|
+
extends ModuleFederationPluginOptions {
|
|
12
|
+
baseURI: Compiler['options']['output']['publicPath'];
|
|
13
|
+
promiseBaseURI?: string;
|
|
14
|
+
remotes: Record<string, string>;
|
|
15
|
+
name?: string;
|
|
16
|
+
asyncChunkLoading: boolean;
|
|
17
|
+
debug?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class DynamicFilesystemChunkLoadingPlugin {
|
|
21
|
+
private options: DynamicFilesystemChunkLoadingOptions;
|
|
22
|
+
private _asyncChunkLoading: boolean;
|
|
23
|
+
|
|
24
|
+
constructor(options: DynamicFilesystemChunkLoadingOptions) {
|
|
25
|
+
this.options = options || ({} as DynamicFilesystemChunkLoadingOptions);
|
|
26
|
+
this._asyncChunkLoading = this.options.asyncChunkLoading;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
apply(compiler: Compiler) {
|
|
30
|
+
const { RuntimeGlobals } = compiler.webpack;
|
|
31
|
+
const chunkLoadingValue = this._asyncChunkLoading
|
|
32
|
+
? 'async-node'
|
|
33
|
+
: 'require';
|
|
34
|
+
|
|
35
|
+
new StartupChunkDependenciesPlugin({
|
|
36
|
+
chunkLoading: chunkLoadingValue,
|
|
37
|
+
asyncChunkLoading: this._asyncChunkLoading,
|
|
38
|
+
//@ts-ignore
|
|
39
|
+
}).apply(compiler);
|
|
40
|
+
|
|
41
|
+
compiler.hooks.thisCompilation.tap(
|
|
42
|
+
'DynamicFilesystemChunkLoadingPlugin',
|
|
43
|
+
(compilation: Compilation) => {
|
|
44
|
+
// Always enabled
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
46
|
+
const isEnabledForChunk = (_: Chunk) => true;
|
|
47
|
+
const onceForChunkSet = new WeakSet();
|
|
48
|
+
|
|
49
|
+
const handler = (chunk: Chunk, set: Set<string>) => {
|
|
50
|
+
if (onceForChunkSet.has(chunk)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
onceForChunkSet.add(chunk);
|
|
55
|
+
if (!isEnabledForChunk(chunk)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
|
59
|
+
set.add(RuntimeGlobals.hasOwnProperty);
|
|
60
|
+
|
|
61
|
+
set.add(RuntimeGlobals.publicPath); // this breaks things
|
|
62
|
+
compilation.addRuntimeModule(
|
|
63
|
+
chunk,
|
|
64
|
+
new ChunkLoadingRuntimeModule(set, this.options, {
|
|
65
|
+
webpack: compiler.webpack,
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
compilation.hooks.runtimeRequirementInTree
|
|
70
|
+
.for(RuntimeGlobals.ensureChunkHandlers)
|
|
71
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
72
|
+
compilation.hooks.runtimeRequirementInTree
|
|
73
|
+
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
|
|
74
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
75
|
+
compilation.hooks.runtimeRequirementInTree
|
|
76
|
+
.for(RuntimeGlobals.hmrDownloadManifest)
|
|
77
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
78
|
+
compilation.hooks.runtimeRequirementInTree
|
|
79
|
+
.for(RuntimeGlobals.baseURI)
|
|
80
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
81
|
+
compilation.hooks.runtimeRequirementInTree
|
|
82
|
+
.for(RuntimeGlobals.externalInstallChunk)
|
|
83
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
84
|
+
compilation.hooks.runtimeRequirementInTree
|
|
85
|
+
.for(RuntimeGlobals.onChunksLoaded)
|
|
86
|
+
.tap('DynamicFilesystemChunkLoadingPlugin', handler);
|
|
87
|
+
compilation.hooks.runtimeRequirementInTree
|
|
88
|
+
.for(RuntimeGlobals.ensureChunkHandlers)
|
|
89
|
+
.tap(
|
|
90
|
+
'DynamicFilesystemChunkLoadingPlugin',
|
|
91
|
+
(chunk: Chunk, set: Set<string>) => {
|
|
92
|
+
if (!isEnabledForChunk(chunk)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
set.add(RuntimeGlobals.getChunkScriptFilename);
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
compilation.hooks.runtimeRequirementInTree
|
|
99
|
+
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
|
|
100
|
+
.tap(
|
|
101
|
+
'DynamicFilesystemChunkLoadingPlugin',
|
|
102
|
+
(chunk: Chunk, set: Set<string>) => {
|
|
103
|
+
if (!isEnabledForChunk(chunk)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
|
|
107
|
+
set.add(RuntimeGlobals.moduleCache);
|
|
108
|
+
set.add(RuntimeGlobals.hmrModuleData);
|
|
109
|
+
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
compilation.hooks.runtimeRequirementInTree
|
|
113
|
+
.for(RuntimeGlobals.hmrDownloadManifest)
|
|
114
|
+
.tap(
|
|
115
|
+
'DynamicFilesystemChunkLoadingPlugin',
|
|
116
|
+
(chunk: Chunk, set: Set<string>) => {
|
|
117
|
+
if (!isEnabledForChunk(chunk)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
set.add(RuntimeGlobals.getUpdateManifestFilename);
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
compilation.hooks.runtimeRequirementInTree
|
|
125
|
+
.for(RuntimeGlobals.publicPath)
|
|
126
|
+
.tap('RuntimePlugin', (chunk, set) => {
|
|
127
|
+
const { outputOptions } = compilation;
|
|
128
|
+
const { publicPath: globalPublicPath, scriptType } = outputOptions;
|
|
129
|
+
const entryOptions = chunk.getEntryOptions();
|
|
130
|
+
const publicPath =
|
|
131
|
+
entryOptions && entryOptions.publicPath !== undefined
|
|
132
|
+
? entryOptions.publicPath
|
|
133
|
+
: globalPublicPath;
|
|
134
|
+
|
|
135
|
+
const module = new AutoPublicPathRuntimeModule(this.options);
|
|
136
|
+
if (publicPath === 'auto' && scriptType !== 'module') {
|
|
137
|
+
set.add(RuntimeGlobals.global);
|
|
138
|
+
} else if (
|
|
139
|
+
typeof publicPath !== 'string' ||
|
|
140
|
+
/\[(full)?hash\]/.test(publicPath)
|
|
141
|
+
) {
|
|
142
|
+
module.fullHash = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
compilation.addRuntimeModule(chunk, module);
|
|
146
|
+
return true;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
compilation.hooks.additionalTreeRuntimeRequirements.tap(
|
|
150
|
+
'StartupChunkDependenciesPlugin',
|
|
151
|
+
(
|
|
152
|
+
chunk: Chunk,
|
|
153
|
+
set: Set<string>,
|
|
154
|
+
{ chunkGraph }: { chunkGraph: ChunkGraph },
|
|
155
|
+
) => {
|
|
156
|
+
// compilation.addRuntimeModule(
|
|
157
|
+
// chunk,
|
|
158
|
+
// //@ts-ignore
|
|
159
|
+
// new FederationModuleInfoRuntimeModule(),
|
|
160
|
+
// );
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default DynamicFilesystemChunkLoadingPlugin;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
3
|
+
*/
|
|
4
|
+
import type { Chunk, Compiler } from 'webpack';
|
|
5
|
+
import { normalizeWebpackPath } from '@module-federation/sdk/normalize-webpack-path';
|
|
6
|
+
const { RuntimeGlobals, RuntimeModule } = require(
|
|
7
|
+
normalizeWebpackPath('webpack'),
|
|
8
|
+
) as typeof import('webpack');
|
|
9
|
+
const { getUndoPath } = require(
|
|
10
|
+
normalizeWebpackPath('webpack/lib/util/identifier'),
|
|
11
|
+
) as typeof import('webpack/lib/util/identifier');
|
|
12
|
+
import { SyncWaterfallHook } from 'tapable';
|
|
13
|
+
const compileBooleanMatcher = require(
|
|
14
|
+
normalizeWebpackPath('webpack/lib/util/compileBooleanMatcher'),
|
|
15
|
+
) as typeof import('webpack/lib/util/compileBooleanMatcher');
|
|
16
|
+
import {
|
|
17
|
+
generateHmrCode,
|
|
18
|
+
getInitialChunkIds,
|
|
19
|
+
generateLoadingCode,
|
|
20
|
+
generateHmrManifestCode,
|
|
21
|
+
handleOnChunkLoad,
|
|
22
|
+
generateLoadScript,
|
|
23
|
+
generateInstallChunk,
|
|
24
|
+
generateExternalInstallChunkCode,
|
|
25
|
+
} from './webpackChunkUtilities';
|
|
26
|
+
import {
|
|
27
|
+
fileSystemRunInContextStrategy,
|
|
28
|
+
httpEvalStrategy,
|
|
29
|
+
httpVmStrategy,
|
|
30
|
+
} from '../filesystem/stratagies';
|
|
31
|
+
|
|
32
|
+
interface RemotesByType {
|
|
33
|
+
functional: string[];
|
|
34
|
+
normal: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface DynamicFilesystemChunkLoadingRuntimeModuleOptions {
|
|
38
|
+
baseURI: Compiler['options']['output']['publicPath'];
|
|
39
|
+
promiseBaseURI?: string;
|
|
40
|
+
remotes: Record<string, string>;
|
|
41
|
+
name?: string;
|
|
42
|
+
debug?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ChunkLoadingContext {
|
|
46
|
+
webpack: Compiler['webpack'];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class DynamicFilesystemChunkLoadingRuntimeModule extends RuntimeModule {
|
|
50
|
+
private runtimeRequirements: Set<string>;
|
|
51
|
+
private options: DynamicFilesystemChunkLoadingRuntimeModuleOptions;
|
|
52
|
+
private chunkLoadingContext: ChunkLoadingContext;
|
|
53
|
+
hooks = {
|
|
54
|
+
strategyCase: new SyncWaterfallHook(['source']),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
runtimeRequirements: Set<string>,
|
|
59
|
+
options: DynamicFilesystemChunkLoadingRuntimeModuleOptions,
|
|
60
|
+
chunkLoadingContext: ChunkLoadingContext,
|
|
61
|
+
) {
|
|
62
|
+
super('readFile chunk loading', RuntimeModule.STAGE_ATTACH + 1);
|
|
63
|
+
this.runtimeRequirements = runtimeRequirements;
|
|
64
|
+
|
|
65
|
+
this.options = options;
|
|
66
|
+
this.chunkLoadingContext = chunkLoadingContext;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @private
|
|
71
|
+
* @param {Chunk} chunk chunk
|
|
72
|
+
* @param {string} rootOutputDir root output directory
|
|
73
|
+
* @returns {string} generated code
|
|
74
|
+
*/
|
|
75
|
+
_generateBaseUri(chunk: Chunk, rootOutputDir: string) {
|
|
76
|
+
const options = chunk.getEntryOptions();
|
|
77
|
+
if (options && options.baseUri) {
|
|
78
|
+
return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `${RuntimeGlobals.baseURI} = require("url").pathToFileURL(${
|
|
82
|
+
rootOutputDir
|
|
83
|
+
? `__dirname + ${JSON.stringify('/' + rootOutputDir)}`
|
|
84
|
+
: '__filename'
|
|
85
|
+
});`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @private
|
|
90
|
+
* @param {unknown[]} items item to log
|
|
91
|
+
*/
|
|
92
|
+
_getLogger(...items: unknown[]) {
|
|
93
|
+
if (!this.options.debug) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return `console.log(${items.join(',')});`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @returns {string} runtime code
|
|
102
|
+
*/
|
|
103
|
+
override generate() {
|
|
104
|
+
const { remotes = {}, name } = this.options;
|
|
105
|
+
const { webpack } = this.chunkLoadingContext;
|
|
106
|
+
const { chunkGraph, chunk, compilation } = this;
|
|
107
|
+
const { Template } = webpack;
|
|
108
|
+
if (!chunkGraph || !chunk || !compilation) {
|
|
109
|
+
console.warn('Missing required properties. Returning empty string.');
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { runtimeTemplate } = compilation;
|
|
114
|
+
const jsModulePlugin =
|
|
115
|
+
webpack?.javascript?.JavascriptModulesPlugin ||
|
|
116
|
+
require('webpack/lib/javascript/JavascriptModulesPlugin');
|
|
117
|
+
const { chunkHasJs } = jsModulePlugin;
|
|
118
|
+
const fn = RuntimeGlobals.ensureChunkHandlers;
|
|
119
|
+
|
|
120
|
+
const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
|
|
121
|
+
const hasJsMatcher = compileBooleanMatcher(conditionMap);
|
|
122
|
+
const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
|
|
123
|
+
|
|
124
|
+
const outputName = compilation.getPath(
|
|
125
|
+
jsModulePlugin.getChunkFilenameTemplate(chunk, compilation.outputOptions),
|
|
126
|
+
{ chunk, contentHashType: 'javascript' },
|
|
127
|
+
);
|
|
128
|
+
const rootOutputDir = getUndoPath(
|
|
129
|
+
outputName,
|
|
130
|
+
compilation.outputOptions.path,
|
|
131
|
+
false,
|
|
132
|
+
);
|
|
133
|
+
const stateExpression = this.runtimeRequirements.has(
|
|
134
|
+
RuntimeGlobals.hmrDownloadUpdateHandlers,
|
|
135
|
+
)
|
|
136
|
+
? `${RuntimeGlobals.hmrRuntimeStatePrefix}_readFileVm`
|
|
137
|
+
: undefined;
|
|
138
|
+
|
|
139
|
+
const dynamicFilesystemChunkLoadingPluginCode = Template.asString([
|
|
140
|
+
fileSystemRunInContextStrategy.toString(),
|
|
141
|
+
httpEvalStrategy.toString(),
|
|
142
|
+
httpVmStrategy.toString(),
|
|
143
|
+
'const loadChunkStrategy = async (strategyType,chunkId,rootOutputDir, remotes, callback) => {',
|
|
144
|
+
Template.indent([
|
|
145
|
+
'switch (strategyType) {',
|
|
146
|
+
Template.indent([
|
|
147
|
+
'case "filesystem": return await fileSystemRunInContextStrategy(chunkId,rootOutputDir, remotes, callback);',
|
|
148
|
+
'case "http-eval": return await httpEvalStrategy(chunkId,rootOutputDir, remotes, callback);',
|
|
149
|
+
'case "http-vm": return await httpVmStrategy(chunkId,rootOutputDir, remotes, callback);',
|
|
150
|
+
this.hooks.strategyCase.call(
|
|
151
|
+
'default: throw new Error("Invalid strategy type");',
|
|
152
|
+
) as string,
|
|
153
|
+
]),
|
|
154
|
+
'}',
|
|
155
|
+
]),
|
|
156
|
+
'};',
|
|
157
|
+
]);
|
|
158
|
+
const remoteRegistry = Template.asString([
|
|
159
|
+
`var remotes = ${JSON.stringify(
|
|
160
|
+
Object.values(remotes).reduce(
|
|
161
|
+
(acc, remote) => {
|
|
162
|
+
const [global, url] = remote.split('@');
|
|
163
|
+
acc[global] = url;
|
|
164
|
+
return acc;
|
|
165
|
+
},
|
|
166
|
+
{} as Record<string, string>,
|
|
167
|
+
),
|
|
168
|
+
)};`,
|
|
169
|
+
Template.asString([
|
|
170
|
+
'Object.keys(remotes).forEach(function(remote) {',
|
|
171
|
+
Template.indent(
|
|
172
|
+
'globalThis.__remote_scope__._config[remote] = remotes[remote];',
|
|
173
|
+
),
|
|
174
|
+
'});',
|
|
175
|
+
]),
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
return Template.asString([
|
|
179
|
+
remoteRegistry,
|
|
180
|
+
dynamicFilesystemChunkLoadingPluginCode,
|
|
181
|
+
this.runtimeRequirements.has(RuntimeGlobals.baseURI)
|
|
182
|
+
? this._generateBaseUri(chunk, rootOutputDir)
|
|
183
|
+
: '// no baseURI',
|
|
184
|
+
'',
|
|
185
|
+
'// object to store loaded chunks',
|
|
186
|
+
'// "0" means "already loaded", Promise means loading',
|
|
187
|
+
`var installedChunks = ${
|
|
188
|
+
stateExpression ? `${stateExpression} = ${stateExpression} || ` : ''
|
|
189
|
+
}{`,
|
|
190
|
+
Template.indent(
|
|
191
|
+
Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 0`).join(
|
|
192
|
+
',\n',
|
|
193
|
+
),
|
|
194
|
+
),
|
|
195
|
+
'};',
|
|
196
|
+
'',
|
|
197
|
+
handleOnChunkLoad(
|
|
198
|
+
this.runtimeRequirements.has(RuntimeGlobals.onChunksLoaded),
|
|
199
|
+
runtimeTemplate,
|
|
200
|
+
),
|
|
201
|
+
'',
|
|
202
|
+
generateInstallChunk(
|
|
203
|
+
runtimeTemplate,
|
|
204
|
+
this.runtimeRequirements.has(RuntimeGlobals.onChunksLoaded),
|
|
205
|
+
),
|
|
206
|
+
'',
|
|
207
|
+
this.runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers)
|
|
208
|
+
? generateLoadScript(runtimeTemplate)
|
|
209
|
+
: '// no remote script loader needed',
|
|
210
|
+
this.runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers)
|
|
211
|
+
? generateLoadingCode(
|
|
212
|
+
this.runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers),
|
|
213
|
+
fn,
|
|
214
|
+
hasJsMatcher,
|
|
215
|
+
rootOutputDir,
|
|
216
|
+
remotes,
|
|
217
|
+
name,
|
|
218
|
+
)
|
|
219
|
+
: '// no chunk loading',
|
|
220
|
+
'',
|
|
221
|
+
generateExternalInstallChunkCode(
|
|
222
|
+
this.runtimeRequirements.has(RuntimeGlobals.externalInstallChunk),
|
|
223
|
+
this.options.debug,
|
|
224
|
+
),
|
|
225
|
+
'',
|
|
226
|
+
generateHmrCode(
|
|
227
|
+
this.runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers),
|
|
228
|
+
rootOutputDir,
|
|
229
|
+
),
|
|
230
|
+
'',
|
|
231
|
+
generateHmrManifestCode(
|
|
232
|
+
this.runtimeRequirements.has(RuntimeGlobals.hmrDownloadManifest),
|
|
233
|
+
rootOutputDir,
|
|
234
|
+
),
|
|
235
|
+
]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default DynamicFilesystemChunkLoadingRuntimeModule;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import type { Compiler, container } from 'webpack';
|
|
4
|
+
import { extractUrlAndGlobal } from '@module-federation/utilities';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Interface for NodeContainerOptions which extends ContainerPluginOptions
|
|
8
|
+
* @interface
|
|
9
|
+
* @property {Record<string, unknown>} experiments - Optional experiments configuration
|
|
10
|
+
* @property {boolean} debug - Optional debug flag
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Interface for Context
|
|
15
|
+
* @interface
|
|
16
|
+
* @property {typeof container.ContainerPlugin} ContainerPlugin - Optional ContainerPlugin
|
|
17
|
+
*/
|
|
18
|
+
interface Context {
|
|
19
|
+
ContainerPlugin?: typeof container.ContainerPlugin;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// possible remote evaluators
|
|
23
|
+
// this depends on the chunk format selected.
|
|
24
|
+
// commonjs2 - it think, is lazily evaluated - beware
|
|
25
|
+
// const remote = eval(scriptContent + '\n try{' + moduleName + '}catch(e) { null; };');
|
|
26
|
+
// commonjs - fine to use but exports marker doesnt exist
|
|
27
|
+
// const remote = eval('let exports = {};' + scriptContent + 'exports');
|
|
28
|
+
// commonjs-module, ideal since it returns a commonjs module format
|
|
29
|
+
// const remote = eval(scriptContent + 'module.exports')
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This function iterates over all remotes and checks if they
|
|
33
|
+
* are internal or not. If it's an internal remote then we add it to our new object
|
|
34
|
+
* with a key of the name of the remote and value as internal. If it's not an internal
|
|
35
|
+
* remote then we check if there is a '@' in that string which likely means it is a global @ url
|
|
36
|
+
*
|
|
37
|
+
* @param {Record<string, any>} remotes - The remotes to parse.
|
|
38
|
+
* @returns {Record<string, string>} - The parsed remotes.
|
|
39
|
+
* */
|
|
40
|
+
|
|
41
|
+
export const parseRemotes = (
|
|
42
|
+
remotes: Record<string, any>,
|
|
43
|
+
): Record<string, string> => {
|
|
44
|
+
if (!remotes || typeof remotes !== 'object') {
|
|
45
|
+
throw new Error('remotes must be an object');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return Object.entries(remotes).reduce(
|
|
49
|
+
(acc: Record<string, string>, [key, value]) => {
|
|
50
|
+
const isInternal = value.startsWith('internal ');
|
|
51
|
+
const isGlobal =
|
|
52
|
+
value.includes('@') &&
|
|
53
|
+
!['window.', 'global.', 'globalThis.', 'self.'].some((prefix) =>
|
|
54
|
+
value.startsWith(prefix),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
acc[key] = isInternal || !isGlobal ? value : parseRemoteSyntax(value);
|
|
58
|
+
|
|
59
|
+
return acc;
|
|
60
|
+
},
|
|
61
|
+
{},
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Parses the remote syntax and returns a formatted string if the remote includes '@' and does not start with 'window', 'global', or 'globalThis'.
|
|
66
|
+
* Otherwise, it returns the original remote string.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} remote - The remote string to parse.
|
|
69
|
+
* @returns {string} - The parsed remote string or the original remote string.
|
|
70
|
+
* @throws {Error} - Throws an error if the remote is not a string.
|
|
71
|
+
*/
|
|
72
|
+
export const parseRemoteSyntax = (remote: any): string => {
|
|
73
|
+
if (typeof remote !== 'string') {
|
|
74
|
+
throw new Error('remote must be a string');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (remote.includes('@')) {
|
|
78
|
+
const [url, global] = extractUrlAndGlobal(remote);
|
|
79
|
+
if (
|
|
80
|
+
!['window.', 'global.', 'globalThis.'].some((prefix) =>
|
|
81
|
+
global.startsWith(prefix),
|
|
82
|
+
)
|
|
83
|
+
) {
|
|
84
|
+
return `globalThis.__remote_scope__.${global}@${url}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return remote;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Class representing a NodeContainerPlugin.
|
|
92
|
+
* @class
|
|
93
|
+
*/
|
|
94
|
+
class NodeContainerPlugin {
|
|
95
|
+
private _options: any;
|
|
96
|
+
private context: Context;
|
|
97
|
+
private experiments: any['experiments'];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create a NodeContainerPlugin.
|
|
101
|
+
* @constructor
|
|
102
|
+
* @param {NodeContainerOptions} options - The options for the NodeContainerPlugin
|
|
103
|
+
* @param {Context} context - The context for the NodeContainerPlugin
|
|
104
|
+
*/
|
|
105
|
+
constructor({ experiments, debug, ...options }: any, context: Context) {
|
|
106
|
+
this._options = options || ({} as any);
|
|
107
|
+
this.context = context || ({} as Context);
|
|
108
|
+
this.experiments = experiments || {};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Apply method for the NodeContainerPlugin class.
|
|
113
|
+
* @method
|
|
114
|
+
* @param {Compiler} compiler - The webpack compiler.
|
|
115
|
+
*/
|
|
116
|
+
apply(compiler: Compiler) {
|
|
117
|
+
// When used with Next.js, context is needed to use Next.js webpack
|
|
118
|
+
const { webpack } = compiler;
|
|
119
|
+
|
|
120
|
+
const pluginOptions = {
|
|
121
|
+
name: this._options.name,
|
|
122
|
+
filename: this._options.filename,
|
|
123
|
+
exposes: this._options.exposes,
|
|
124
|
+
library: this._options.library,
|
|
125
|
+
runtime: this._options.runtime,
|
|
126
|
+
shareScope: this._options.shareScope,
|
|
127
|
+
};
|
|
128
|
+
// //TODO can use import meta mock object but need to update data structure of remote_scope
|
|
129
|
+
// if (compiler.options && compiler.options.output) {
|
|
130
|
+
// compiler.options.output.importMetaName = 'remoteContainerRegistry';
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
// const chunkFileName = compiler.options?.output?.chunkFilename;
|
|
134
|
+
// const uniqueName =
|
|
135
|
+
// compiler?.options?.output?.uniqueName || this._options.name;
|
|
136
|
+
|
|
137
|
+
// if (typeof chunkFileName === 'string' && uniqueName && !chunkFileName.includes(uniqueName)) {
|
|
138
|
+
// const suffix = `-[chunkhash].js`;
|
|
139
|
+
// compiler.options.output.chunkFilename = chunkFileName.replace('.js', suffix);
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
new (this.context.ContainerPlugin ||
|
|
143
|
+
(webpack && webpack.container.ContainerPlugin) ||
|
|
144
|
+
require('webpack/lib/container/ContainerPlugin'))(pluginOptions).apply(
|
|
145
|
+
compiler,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default NodeContainerPlugin;
|