@shuvi/toolpack 2.0.0-dev.12 → 2.0.0-dev.14
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/lib/webpack/dynamic-dll/bundler/rspack-bundler.d.ts +22 -0
- package/lib/webpack/dynamic-dll/bundler/rspack-bundler.js +211 -0
- package/lib/webpack/dynamic-dll/bundler/rspack-config.d.ts +16 -0
- package/lib/webpack/dynamic-dll/bundler/rspack-config.js +140 -0
- package/lib/webpack/dynamic-dll/plugin/rspack-dynamic-dll-plugin.d.ts +20 -0
- package/lib/webpack/dynamic-dll/plugin/rspack-dynamic-dll-plugin.js +90 -0
- package/lib/webpack/dynamic-dll/rspack-dynamic-dll.d.ts +35 -0
- package/lib/webpack/dynamic-dll/rspack-dynamic-dll.js +158 -0
- package/lib/webpack/rspack.d.ts +3 -7
- package/lib/webpack/rspack.js +4 -2
- package/package.json +4 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Configuration } from '@rspack/core';
|
|
2
|
+
import RspackChain from 'rspack-chain';
|
|
3
|
+
import { ModuleSnapshot } from '../moduleCollector';
|
|
4
|
+
export interface BuildOptions {
|
|
5
|
+
outputDir: string;
|
|
6
|
+
configRspack?: (chain: RspackChain) => RspackChain;
|
|
7
|
+
shared?: ShareConfig;
|
|
8
|
+
externals?: Configuration['externals'];
|
|
9
|
+
esmFullSpecific?: Boolean;
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export type ShareConfig = Record<string, any>;
|
|
13
|
+
type OnBuildComplete = (error?: null | Error) => void;
|
|
14
|
+
export declare class RspackBundler {
|
|
15
|
+
private _nextBuild;
|
|
16
|
+
private _completeFns;
|
|
17
|
+
private _isBuilding;
|
|
18
|
+
build(snapshot: ModuleSnapshot, options: BuildOptions): Promise<void>;
|
|
19
|
+
private _buildDll;
|
|
20
|
+
onBuildComplete(fn: OnBuildComplete): void;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RspackBundler = void 0;
|
|
13
|
+
const core_1 = require("@rspack/core");
|
|
14
|
+
const fs_extra_1 = require("fs-extra");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
const crypto_1 = require("crypto");
|
|
17
|
+
const constants_1 = require("../constants");
|
|
18
|
+
const dep_1 = require("../dep");
|
|
19
|
+
const metadata_1 = require("../metadata");
|
|
20
|
+
const utils_1 = require("../utils");
|
|
21
|
+
const rspack_config_1 = require("./rspack-config");
|
|
22
|
+
function getHash(text) {
|
|
23
|
+
return (0, crypto_1.createHash)('sha256').update(text).digest('hex').substring(0, 8);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* hash everything that can change the build result
|
|
27
|
+
*
|
|
28
|
+
* @param {BuildOptions} options
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function getMainHash(options) {
|
|
32
|
+
let content = JSON.stringify({
|
|
33
|
+
shared: options.shared
|
|
34
|
+
});
|
|
35
|
+
return getHash([utils_1.version, content].join(''));
|
|
36
|
+
}
|
|
37
|
+
function getBuildHash(hash, snapshot) {
|
|
38
|
+
return getHash(hash + JSON.stringify(snapshot));
|
|
39
|
+
}
|
|
40
|
+
function getRspackConfigFromOptions({ deps, entry, outputDir, shared, externals, esmFullSpecific }) {
|
|
41
|
+
const exposes = deps.reduce((memo, dep) => {
|
|
42
|
+
memo[`./${dep.request}`] = dep.filename;
|
|
43
|
+
return memo;
|
|
44
|
+
}, {});
|
|
45
|
+
const chain = (0, rspack_config_1.getRspackConfig)({
|
|
46
|
+
name: constants_1.NAME,
|
|
47
|
+
entry,
|
|
48
|
+
filename: constants_1.DLL_FILENAME,
|
|
49
|
+
outputDir,
|
|
50
|
+
publicPath: constants_1.DEFAULT_PUBLIC_PATH,
|
|
51
|
+
shared,
|
|
52
|
+
externals,
|
|
53
|
+
esmFullSpecific,
|
|
54
|
+
exposes
|
|
55
|
+
});
|
|
56
|
+
return chain.toConfig();
|
|
57
|
+
}
|
|
58
|
+
function buildDeps(_a) {
|
|
59
|
+
return __awaiter(this, arguments, void 0, function* ({ deps, dir }) {
|
|
60
|
+
(0, fs_extra_1.mkdirpSync)(dir);
|
|
61
|
+
// expose files
|
|
62
|
+
yield Promise.all(deps.map((dep) => __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
const content = yield dep.buildExposeContent();
|
|
64
|
+
yield (0, fs_extra_1.writeFile)(dep.filename, content, 'utf-8');
|
|
65
|
+
})));
|
|
66
|
+
// index file - export all modules using valid identifiers
|
|
67
|
+
const indexContent = deps
|
|
68
|
+
.map(dep => {
|
|
69
|
+
const moduleName = dep.request;
|
|
70
|
+
const filename = dep.filename;
|
|
71
|
+
// Convert module name to valid identifier
|
|
72
|
+
const validIdentifier = moduleName.replace(/[^a-zA-Z0-9_$]/g, '_');
|
|
73
|
+
return `export { default as ${validIdentifier} } from '${filename}';`;
|
|
74
|
+
})
|
|
75
|
+
.join('\n');
|
|
76
|
+
(0, fs_extra_1.writeFileSync)((0, path_1.join)(dir, 'index.js'), indexContent, 'utf-8');
|
|
77
|
+
return deps;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function rspackBuild(config) {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const compiler = (0, core_1.rspack)(config);
|
|
84
|
+
compiler.run((err, stats) => {
|
|
85
|
+
if (err || (stats === null || stats === void 0 ? void 0 : stats.hasErrors())) {
|
|
86
|
+
if (err) {
|
|
87
|
+
reject(err);
|
|
88
|
+
}
|
|
89
|
+
if (stats) {
|
|
90
|
+
const errorMsg = stats.toString('errors-only');
|
|
91
|
+
reject(new Error(errorMsg));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
resolve(stats);
|
|
96
|
+
}
|
|
97
|
+
compiler.close(() => { });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function isSnapshotSame(pre, cur) {
|
|
103
|
+
const keys = Object.keys(cur);
|
|
104
|
+
for (let index = 0; index < keys.length; index++) {
|
|
105
|
+
const id = keys[index];
|
|
106
|
+
const preItem = pre[id];
|
|
107
|
+
const nextItem = cur[id];
|
|
108
|
+
if (!preItem) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (preItem.version !== nextItem.version) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
class RspackBundler {
|
|
118
|
+
constructor() {
|
|
119
|
+
this._nextBuild = null;
|
|
120
|
+
this._completeFns = [];
|
|
121
|
+
this._isBuilding = false;
|
|
122
|
+
}
|
|
123
|
+
build(snapshot, options) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
if (this._isBuilding) {
|
|
126
|
+
this._nextBuild = snapshot;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let error = null;
|
|
130
|
+
this._isBuilding = true;
|
|
131
|
+
try {
|
|
132
|
+
yield this._buildDll(snapshot, options);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
error = err;
|
|
136
|
+
}
|
|
137
|
+
this._isBuilding = false;
|
|
138
|
+
if (error) {
|
|
139
|
+
console.error(`[@shuvi/dll]: Rspack Bundle Error`);
|
|
140
|
+
console.error(error);
|
|
141
|
+
}
|
|
142
|
+
this._completeFns.forEach(fn => fn(error));
|
|
143
|
+
this._completeFns = [];
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
_buildDll(snapshot, options) {
|
|
147
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
const { externals = {}, shared = {}, outputDir, force, esmFullSpecific = true } = options;
|
|
149
|
+
const mainHash = getMainHash(options);
|
|
150
|
+
const dllDir = (0, utils_1.getDllDir)(outputDir);
|
|
151
|
+
const preMetadata = (0, metadata_1.getMetadata)(outputDir);
|
|
152
|
+
const metadata = {
|
|
153
|
+
hash: mainHash,
|
|
154
|
+
buildHash: preMetadata.buildHash,
|
|
155
|
+
modules: snapshot
|
|
156
|
+
};
|
|
157
|
+
if (!force &&
|
|
158
|
+
preMetadata.hash === metadata.hash &&
|
|
159
|
+
isSnapshotSame(preMetadata.modules, snapshot)) {
|
|
160
|
+
return [false, preMetadata];
|
|
161
|
+
}
|
|
162
|
+
const dllPendingDir = (0, utils_1.getDllPendingDir)(outputDir);
|
|
163
|
+
// create a temporal dir to build. This avoids leaving the dll
|
|
164
|
+
// in a corrupted state if there is an error during the build
|
|
165
|
+
if ((0, fs_extra_1.existsSync)(dllPendingDir)) {
|
|
166
|
+
(0, fs_extra_1.emptyDirSync)(dllPendingDir);
|
|
167
|
+
}
|
|
168
|
+
const depsDir = (0, utils_1.getDepsDir)(dllPendingDir);
|
|
169
|
+
const deps = Object.entries(snapshot).map(([request, { version, libraryPath }]) => {
|
|
170
|
+
return new dep_1.Dep({
|
|
171
|
+
request,
|
|
172
|
+
libraryPath,
|
|
173
|
+
version,
|
|
174
|
+
outputPath: depsDir
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
yield buildDeps({
|
|
178
|
+
deps,
|
|
179
|
+
dir: depsDir
|
|
180
|
+
});
|
|
181
|
+
yield rspackBuild(getRspackConfigFromOptions({
|
|
182
|
+
deps,
|
|
183
|
+
entry: (0, path_1.join)(depsDir, 'index.js'),
|
|
184
|
+
shared,
|
|
185
|
+
externals,
|
|
186
|
+
esmFullSpecific,
|
|
187
|
+
outputDir: dllPendingDir
|
|
188
|
+
}));
|
|
189
|
+
if (this._nextBuild) {
|
|
190
|
+
const param = this._nextBuild;
|
|
191
|
+
this._nextBuild = null;
|
|
192
|
+
return yield this._buildDll(param, options);
|
|
193
|
+
}
|
|
194
|
+
metadata.buildHash = getBuildHash(metadata.hash, snapshot);
|
|
195
|
+
// finish build
|
|
196
|
+
(0, metadata_1.writeMetadata)(dllPendingDir, metadata);
|
|
197
|
+
(0, fs_extra_1.removeSync)(dllDir);
|
|
198
|
+
(0, fs_extra_1.renameSync)(dllPendingDir, dllDir);
|
|
199
|
+
return [true, metadata];
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
onBuildComplete(fn) {
|
|
203
|
+
if (this._isBuilding) {
|
|
204
|
+
this._completeFns.push(fn);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
fn();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.RspackBundler = RspackBundler;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import RspackChain from 'rspack-chain';
|
|
2
|
+
import type { Configuration } from '@rspack/core';
|
|
3
|
+
type ShareConfig = Record<string, any>;
|
|
4
|
+
export interface ConfigOptions {
|
|
5
|
+
name: string;
|
|
6
|
+
entry: string;
|
|
7
|
+
filename: string;
|
|
8
|
+
outputDir: string;
|
|
9
|
+
publicPath: string;
|
|
10
|
+
shared?: ShareConfig;
|
|
11
|
+
externals: Configuration['externals'];
|
|
12
|
+
esmFullSpecific: Boolean;
|
|
13
|
+
exposes: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export declare function getRspackConfig({ name, entry, filename, outputDir, publicPath, shared, externals, esmFullSpecific, exposes }: ConfigOptions): RspackChain;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.getRspackConfig = getRspackConfig;
|
|
30
|
+
const rspack = __importStar(require("@rspack/core"));
|
|
31
|
+
const rspack_chain_1 = __importDefault(require("rspack-chain"));
|
|
32
|
+
const moduleFileExtensions = [
|
|
33
|
+
'.web.mjs',
|
|
34
|
+
'.mjs',
|
|
35
|
+
'.web.js',
|
|
36
|
+
'.js',
|
|
37
|
+
'.json',
|
|
38
|
+
'.web.jsx',
|
|
39
|
+
'.jsx'
|
|
40
|
+
];
|
|
41
|
+
function getRspackConfig({ name, entry, filename, outputDir, publicPath, shared, externals, esmFullSpecific, exposes }) {
|
|
42
|
+
const config = new rspack_chain_1.default();
|
|
43
|
+
config.mode('development');
|
|
44
|
+
config.entry('main').add(entry);
|
|
45
|
+
config.devtool('cheap-module-source-map');
|
|
46
|
+
config.bail(true);
|
|
47
|
+
config.watch(false);
|
|
48
|
+
config.set('infrastructureLogging', {
|
|
49
|
+
level: 'none'
|
|
50
|
+
});
|
|
51
|
+
config.output.merge({
|
|
52
|
+
pathinfo: false,
|
|
53
|
+
path: outputDir,
|
|
54
|
+
filename: filename,
|
|
55
|
+
chunkFilename: '[name].js',
|
|
56
|
+
publicPath,
|
|
57
|
+
uniqueName: name
|
|
58
|
+
});
|
|
59
|
+
config.performance.hints(false);
|
|
60
|
+
config.optimization.merge({
|
|
61
|
+
emitOnErrors: true,
|
|
62
|
+
// need to use DefinePlugin to set process.env.NODE_ENV
|
|
63
|
+
nodeEnv: false,
|
|
64
|
+
runtimeChunk: false,
|
|
65
|
+
minimize: false,
|
|
66
|
+
realContentHash: false
|
|
67
|
+
});
|
|
68
|
+
// Disable splitChunks to avoid chunk naming conflicts
|
|
69
|
+
config.optimization.splitChunks(false);
|
|
70
|
+
config.resolve.extensions.merge(moduleFileExtensions);
|
|
71
|
+
// 添加 Node.js polyfills for browser environment
|
|
72
|
+
config.resolve.fallback.merge({
|
|
73
|
+
stream: false,
|
|
74
|
+
util: false,
|
|
75
|
+
buffer: false,
|
|
76
|
+
process: false,
|
|
77
|
+
assert: false,
|
|
78
|
+
crypto: false,
|
|
79
|
+
fs: false,
|
|
80
|
+
path: false,
|
|
81
|
+
os: false,
|
|
82
|
+
http: false,
|
|
83
|
+
https: false,
|
|
84
|
+
zlib: false,
|
|
85
|
+
url: false,
|
|
86
|
+
querystring: false,
|
|
87
|
+
events: false,
|
|
88
|
+
tty: false,
|
|
89
|
+
net: false,
|
|
90
|
+
child_process: false
|
|
91
|
+
});
|
|
92
|
+
// config.module.set('strictExportPresence', true); // Not supported in Rspack
|
|
93
|
+
// x-ref: https://github.com/webpack/webpack/issues/11467
|
|
94
|
+
if (!esmFullSpecific) {
|
|
95
|
+
config.module
|
|
96
|
+
.rule('rspackPatch')
|
|
97
|
+
.test(/\.m?js/)
|
|
98
|
+
.resolve.set('fullySpecified', false);
|
|
99
|
+
}
|
|
100
|
+
config.module
|
|
101
|
+
.rule('js')
|
|
102
|
+
.test(/\.(js|mjs|cjs|jsx)$/)
|
|
103
|
+
.use('esbuild-loader')
|
|
104
|
+
.loader(require.resolve('esbuild-loader'))
|
|
105
|
+
.options({
|
|
106
|
+
loader: 'jsx', // Remove this if you're not using JSX
|
|
107
|
+
target: 'es2015' // Syntax to compile to (see options below for possible values)
|
|
108
|
+
});
|
|
109
|
+
// Rspack cache configuration - simplified for now
|
|
110
|
+
// TODO: Implement proper cache configuration when Rspack supports it
|
|
111
|
+
config.cache(false);
|
|
112
|
+
config.plugin('private/ignore-plugin').use(rspack.IgnorePlugin, [
|
|
113
|
+
{
|
|
114
|
+
resourceRegExp: /^\.\/locale$/,
|
|
115
|
+
contextRegExp: /moment$/
|
|
116
|
+
}
|
|
117
|
+
]);
|
|
118
|
+
config.plugin('define').use(rspack.DefinePlugin, [
|
|
119
|
+
{
|
|
120
|
+
'process.env.NODE_ENV': JSON.stringify('development')
|
|
121
|
+
}
|
|
122
|
+
]);
|
|
123
|
+
// 使用 ModuleFederationPlugin 来暴露模块
|
|
124
|
+
config
|
|
125
|
+
.plugin('module-federation')
|
|
126
|
+
.use(rspack.container.ModuleFederationPlugin, [
|
|
127
|
+
{
|
|
128
|
+
name: name,
|
|
129
|
+
filename: 'remoteEntry.js',
|
|
130
|
+
exposes: exposes,
|
|
131
|
+
shared: {
|
|
132
|
+
// 共享依赖配置
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
]);
|
|
136
|
+
if (externals) {
|
|
137
|
+
config.externals(externals);
|
|
138
|
+
}
|
|
139
|
+
return config;
|
|
140
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Compiler } from '@rspack/core';
|
|
2
|
+
import { ModuleCollectorOptions, ModuleSnapshot } from '../moduleCollector';
|
|
3
|
+
export type SnapshotListener = (snapshot: ModuleSnapshot) => void;
|
|
4
|
+
export interface RspackDynamicDLLPluginOptions {
|
|
5
|
+
resolveRspackModule: (s: string) => ReturnType<typeof require>;
|
|
6
|
+
dllName: string;
|
|
7
|
+
onSnapshot: SnapshotListener;
|
|
8
|
+
shareScope?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RspackDynamicDLLPlugin {
|
|
11
|
+
private _collector;
|
|
12
|
+
private _dllName;
|
|
13
|
+
private _timer;
|
|
14
|
+
private _matchCache;
|
|
15
|
+
private _onSnapshot;
|
|
16
|
+
private _disabled;
|
|
17
|
+
constructor(opts: RspackDynamicDLLPluginOptions & ModuleCollectorOptions);
|
|
18
|
+
disableDllReference(): void;
|
|
19
|
+
apply(compiler: Compiler): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RspackDynamicDLLPlugin = void 0;
|
|
4
|
+
const moduleCollector_1 = require("../moduleCollector");
|
|
5
|
+
const PLUGIN_NAME = 'RspackDLLBuildDeps';
|
|
6
|
+
class RspackDynamicDLLPlugin {
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
this._dllName = opts.dllName;
|
|
9
|
+
this._onSnapshot = opts.onSnapshot;
|
|
10
|
+
this._collector = (0, moduleCollector_1.getModuleCollector)({
|
|
11
|
+
include: opts.include,
|
|
12
|
+
exclude: opts.exclude
|
|
13
|
+
});
|
|
14
|
+
this._matchCache = new Map();
|
|
15
|
+
this._timer = null;
|
|
16
|
+
this._disabled = false;
|
|
17
|
+
}
|
|
18
|
+
disableDllReference() {
|
|
19
|
+
this._disabled = true;
|
|
20
|
+
}
|
|
21
|
+
apply(compiler) {
|
|
22
|
+
compiler.hooks.normalModuleFactory.tap(PLUGIN_NAME, nmf => {
|
|
23
|
+
nmf.hooks.beforeResolve.tap(PLUGIN_NAME, resolveData => {
|
|
24
|
+
const { request } = resolveData;
|
|
25
|
+
// Check cache first
|
|
26
|
+
const replaceValue = this._matchCache.get(request);
|
|
27
|
+
if (replaceValue) {
|
|
28
|
+
resolveData.request = replaceValue;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// For beforeResolve, we can't use full ModuleCollector logic since we don't have resource yet
|
|
32
|
+
// So we'll do a basic check for node_modules requests and let createModule handle the rest
|
|
33
|
+
if (!this._disabled &&
|
|
34
|
+
request &&
|
|
35
|
+
!request.startsWith('.') &&
|
|
36
|
+
!request.startsWith('/')) {
|
|
37
|
+
// Basic check: if it looks like a node_modules request, we'll redirect it
|
|
38
|
+
// The actual filtering will happen in createModule
|
|
39
|
+
const name = this._dllName;
|
|
40
|
+
const dllRequest = `${name}/${request}`;
|
|
41
|
+
resolveData.request = dllRequest;
|
|
42
|
+
this._matchCache.set(request, dllRequest);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
nmf.hooks.createModule.tap(PLUGIN_NAME, (_createData, resolveData) => {
|
|
46
|
+
const createData = (resolveData === null || resolveData === void 0 ? void 0 : resolveData.createData) || {};
|
|
47
|
+
const request = (resolveData === null || resolveData === void 0 ? void 0 : resolveData.request) || '';
|
|
48
|
+
const resource = (createData === null || createData === void 0 ? void 0 : createData.resource) || '';
|
|
49
|
+
const context = (resolveData === null || resolveData === void 0 ? void 0 : resolveData.context) || '';
|
|
50
|
+
if (!this._collector.shouldCollect({
|
|
51
|
+
request,
|
|
52
|
+
context,
|
|
53
|
+
resource
|
|
54
|
+
})) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const resourceResolveData = (createData === null || createData === void 0 ? void 0 : createData.resourceResolveData) || {};
|
|
58
|
+
const descriptionFileData = (resourceResolveData === null || resourceResolveData === void 0 ? void 0 : resourceResolveData.descriptionFileData) || {};
|
|
59
|
+
const version = (descriptionFileData === null || descriptionFileData === void 0 ? void 0 : descriptionFileData.version) || null;
|
|
60
|
+
this._collector.add(request, {
|
|
61
|
+
libraryPath: resource,
|
|
62
|
+
version
|
|
63
|
+
});
|
|
64
|
+
if (this._disabled) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const name = this._dllName;
|
|
68
|
+
const replaceValue = `${name}/${request}`;
|
|
69
|
+
if ((resolveData === null || resolveData === void 0 ? void 0 : resolveData.request) !== undefined) {
|
|
70
|
+
resolveData.request = replaceValue;
|
|
71
|
+
}
|
|
72
|
+
this._matchCache.set(request, replaceValue);
|
|
73
|
+
// For Rspack, we'll use Module Federation approach rather than RemoteModule
|
|
74
|
+
// The actual module federation is handled by the MF plugin in the config
|
|
75
|
+
return undefined; // Let Rspack handle the module creation
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
compiler.hooks.done.tap(PLUGIN_NAME, (stats) => {
|
|
79
|
+
if (!stats.hasErrors()) {
|
|
80
|
+
if (this._timer) {
|
|
81
|
+
clearTimeout(this._timer);
|
|
82
|
+
}
|
|
83
|
+
this._timer = setTimeout(() => {
|
|
84
|
+
this._onSnapshot(this._collector.snapshot());
|
|
85
|
+
}, 500);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.RspackDynamicDLLPlugin = RspackDynamicDLLPlugin;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
import type * as rspackType from '@rspack/core';
|
|
3
|
+
import type { Configuration } from '@rspack/core';
|
|
4
|
+
import type RspackChain from 'rspack-chain';
|
|
5
|
+
import { ShareConfig } from './bundler/rspack-bundler';
|
|
6
|
+
type IResolveRspackModule = <T extends string>(path: T) => T extends `@rspack/core` ? typeof rspackType : T extends `@rspack/core/${infer R}` ? any : never;
|
|
7
|
+
interface IOpts {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
rootDir: string;
|
|
10
|
+
cacheDir: string;
|
|
11
|
+
resolveRspackModule?: IResolveRspackModule;
|
|
12
|
+
include?: RegExp[];
|
|
13
|
+
exclude?: RegExp[];
|
|
14
|
+
shared?: ShareConfig;
|
|
15
|
+
externals?: Configuration['externals'];
|
|
16
|
+
esmFullSpecific?: Boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class RspackDynamicDll {
|
|
19
|
+
private _opts;
|
|
20
|
+
private _bundler;
|
|
21
|
+
private _rootDir;
|
|
22
|
+
private _cacheDir;
|
|
23
|
+
private _resolveRspackModule;
|
|
24
|
+
private _dllPlugin;
|
|
25
|
+
private _hasBuilt;
|
|
26
|
+
constructor(opts: IOpts);
|
|
27
|
+
private getRemovedModules;
|
|
28
|
+
private handleSnapshot;
|
|
29
|
+
middleware: (req: IncomingMessage, res: ServerResponse, next: (...args: any[]) => any) => Promise<any>;
|
|
30
|
+
modifyRspackChain: (chain: RspackChain) => RspackChain;
|
|
31
|
+
modifyRspack: (config: Configuration) => Configuration;
|
|
32
|
+
private _buildDLL;
|
|
33
|
+
private _getMFconfig;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RspackDynamicDll = void 0;
|
|
16
|
+
const fs_extra_1 = require("fs-extra");
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
const mrmime_1 = require("mrmime");
|
|
19
|
+
const core_1 = __importDefault(require("@rspack/core"));
|
|
20
|
+
const constants_1 = require("./constants");
|
|
21
|
+
const rspack_bundler_1 = require("./bundler/rspack-bundler");
|
|
22
|
+
const rspack_dynamic_dll_plugin_1 = require("./plugin/rspack-dynamic-dll-plugin");
|
|
23
|
+
const metadata_1 = require("./metadata");
|
|
24
|
+
const utils_1 = require("./utils");
|
|
25
|
+
const check_not_in_node_modules_1 = require("./helper/check-not-in-node-modules");
|
|
26
|
+
class RspackDynamicDll {
|
|
27
|
+
constructor(opts) {
|
|
28
|
+
this._hasBuilt = false;
|
|
29
|
+
this.handleSnapshot = (snapshot) => __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
if (this._hasBuilt) {
|
|
31
|
+
(0, metadata_1.writeUpdate)(this._cacheDir, snapshot);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const originModules = (0, metadata_1.getMetadata)(this._cacheDir).modules;
|
|
35
|
+
const diffNames = this.getRemovedModules(snapshot, originModules);
|
|
36
|
+
const requiredSnapshot = Object.assign(Object.assign({}, originModules), snapshot);
|
|
37
|
+
diffNames.forEach(lib => {
|
|
38
|
+
delete requiredSnapshot[lib];
|
|
39
|
+
});
|
|
40
|
+
yield this._buildDLL(requiredSnapshot);
|
|
41
|
+
this._dllPlugin.disableDllReference();
|
|
42
|
+
this._hasBuilt = true;
|
|
43
|
+
});
|
|
44
|
+
this.middleware = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const url = req.url || '';
|
|
46
|
+
const shouldServe = url.startsWith(constants_1.DEFAULT_PUBLIC_PATH);
|
|
47
|
+
if (!shouldServe) {
|
|
48
|
+
return next();
|
|
49
|
+
}
|
|
50
|
+
this._bundler.onBuildComplete(() => {
|
|
51
|
+
const relativePath = url.replace(new RegExp(`^${constants_1.DEFAULT_PUBLIC_PATH}`), '/');
|
|
52
|
+
const filePath = (0, path_1.join)((0, utils_1.getDllDir)(this._cacheDir), relativePath);
|
|
53
|
+
const { mtime } = (0, fs_extra_1.statSync)(filePath);
|
|
54
|
+
// Get the last modification time of the file and convert the time into a world time string
|
|
55
|
+
let lastModified = mtime.toUTCString();
|
|
56
|
+
const ifModifiedSince = req.headers['if-modified-since'];
|
|
57
|
+
// Tell the browser what time to use the browser cache without asking the server directly, but it seems that it is not effective, and needs to learn why.
|
|
58
|
+
res.setHeader('cache-control', 'no-cache');
|
|
59
|
+
if (ifModifiedSince && lastModified <= ifModifiedSince) {
|
|
60
|
+
// If the request header contains the request ifModifiedSince and the file is not modified, it returns 304
|
|
61
|
+
res.writeHead(304, 'Not Modified');
|
|
62
|
+
res.end();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Return the header Last-Modified for the last modification time of the current request file
|
|
66
|
+
res.setHeader('Last-Modified', lastModified);
|
|
67
|
+
// Return file
|
|
68
|
+
res.setHeader('content-type', (0, mrmime_1.lookup)((0, path_1.extname)(url)) || 'text/plain');
|
|
69
|
+
const content = (0, fs_extra_1.readFileSync)(filePath);
|
|
70
|
+
res.statusCode = 200;
|
|
71
|
+
res.end(content);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
this.modifyRspackChain = (chain) => {
|
|
75
|
+
// Keep entries as-is for Rspack since we don't have virtual modules support
|
|
76
|
+
chain
|
|
77
|
+
.plugin('dynamic-dll-mf')
|
|
78
|
+
.use(core_1.default.container.ModuleFederationPlugin, [this._getMFconfig()]);
|
|
79
|
+
chain.plugin('dynamic-dll-plugin').use(this._dllPlugin);
|
|
80
|
+
return chain;
|
|
81
|
+
};
|
|
82
|
+
this.modifyRspack = (config) => {
|
|
83
|
+
// Keep config.entry as-is for Rspack since we don't have virtual modules support
|
|
84
|
+
if (!config.plugins) {
|
|
85
|
+
config.plugins = [];
|
|
86
|
+
}
|
|
87
|
+
config.plugins.push(new core_1.default.container.ModuleFederationPlugin(this._getMFconfig()), this._dllPlugin);
|
|
88
|
+
return config;
|
|
89
|
+
};
|
|
90
|
+
this._opts = opts;
|
|
91
|
+
this._cacheDir = opts.cacheDir;
|
|
92
|
+
this._rootDir = opts.rootDir;
|
|
93
|
+
this._resolveRspackModule = opts.resolveRspackModule || require;
|
|
94
|
+
this._bundler = new rspack_bundler_1.RspackBundler();
|
|
95
|
+
this._dllPlugin = new rspack_dynamic_dll_plugin_1.RspackDynamicDLLPlugin({
|
|
96
|
+
include: opts.include,
|
|
97
|
+
exclude: opts.exclude,
|
|
98
|
+
dllName: constants_1.NAME,
|
|
99
|
+
resolveRspackModule: this._resolveRspackModule, // Type compatibility
|
|
100
|
+
onSnapshot: this.handleSnapshot
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
getRemovedModules(snapshot, originModules) {
|
|
104
|
+
return Object.keys(originModules).filter(key => {
|
|
105
|
+
if (snapshot[key]) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return (0, check_not_in_node_modules_1.checkNotInNodeModules)(key, this._rootDir);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
_buildDLL(snapshot) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
yield this._bundler.build(snapshot, {
|
|
114
|
+
outputDir: this._cacheDir,
|
|
115
|
+
shared: this._opts.shared,
|
|
116
|
+
externals: this._opts.externals,
|
|
117
|
+
esmFullSpecific: this._opts.esmFullSpecific,
|
|
118
|
+
force: process.env.DLL_FORCE_BUILD === 'true'
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
_getMFconfig() {
|
|
123
|
+
return {
|
|
124
|
+
name: '__',
|
|
125
|
+
remotes: {
|
|
126
|
+
// [NAME]: `${NAME}@${DEFAULT_PUBLIC_PATH}${DLL_FILENAME}`,
|
|
127
|
+
// https://webpack.js.org/concepts/module-federation/#promise-based-dynamic-remotes
|
|
128
|
+
[constants_1.NAME]: `
|
|
129
|
+
promise new Promise(resolve => {
|
|
130
|
+
const remoteUrl = '${constants_1.DEFAULT_PUBLIC_PATH}${constants_1.DLL_FILENAME}';
|
|
131
|
+
const script = document.createElement('script');
|
|
132
|
+
script.src = remoteUrl;
|
|
133
|
+
script.onload = () => {
|
|
134
|
+
// the injected script has loaded and is available on window
|
|
135
|
+
// we can now resolve this Promise
|
|
136
|
+
const proxy = {
|
|
137
|
+
get: (request) => {
|
|
138
|
+
const promise = window['${constants_1.NAME}'].get(request);
|
|
139
|
+
return promise;
|
|
140
|
+
},
|
|
141
|
+
init: (arg) => {
|
|
142
|
+
try {
|
|
143
|
+
return window['${constants_1.NAME}'].init(arg);
|
|
144
|
+
} catch(e) {
|
|
145
|
+
// remote container already initialized
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
resolve(proxy);
|
|
150
|
+
}
|
|
151
|
+
// inject this script with the src set to the versioned remoteEntry.js
|
|
152
|
+
document.head.appendChild(script);
|
|
153
|
+
})`.trim()
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.RspackDynamicDll = RspackDynamicDll;
|
package/lib/webpack/rspack.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { rspack, ChunkGroup } from '@rspack/core';
|
|
2
2
|
export { RspackChain } from './config/base.rspack';
|
|
3
|
-
|
|
4
|
-
* @todo back to DLL topic later
|
|
5
|
-
*/
|
|
3
|
+
export { RspackDynamicDll } from './dynamic-dll/rspack-dynamic-dll';
|
|
6
4
|
export { rspack, ChunkGroup };
|
|
7
5
|
/**
|
|
8
6
|
* re-export for shuvi plugin
|
|
@@ -172,12 +170,10 @@ StatsCompilation, StatsError,
|
|
|
172
170
|
* @unsupported Rspack does not export StatsLoggingEntry.
|
|
173
171
|
* TODO: Remove or replace after Rspack support is available.
|
|
174
172
|
*/
|
|
175
|
-
StatsModule
|
|
176
|
-
/**
|
|
173
|
+
StatsModule } from /**
|
|
177
174
|
* @unsupported Rspack does not export StatsModuleIssuer.
|
|
178
175
|
* TODO: Remove or replace after Rspack support is available.
|
|
179
|
-
*/
|
|
180
|
-
} from
|
|
176
|
+
*/
|
|
181
177
|
/**
|
|
182
178
|
* @unsupported Rspack does not export StatsModuleReason.
|
|
183
179
|
* TODO: Remove or replace after Rspack support is available.
|
package/lib/webpack/rspack.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.MultiStats = exports.WebpackOptionsApply = exports.WebpackError = exports.Template = exports.Stats = exports.SourceMapDevToolPlugin = void 0;
|
|
3
|
+
exports.RuntimeModule = exports.ProvidePlugin = exports.ProgressPlugin = exports.MultiCompiler = exports.NormalModuleReplacementPlugin = exports.NormalModule = exports.NoEmitOnErrorsPlugin = exports.Module = exports.LoaderTargetPlugin = exports.LoaderOptionsPlugin = exports.IgnorePlugin = exports.HotModuleReplacementPlugin = exports.ExternalsPlugin = exports.ExternalModule = exports.EvalSourceMapDevToolPlugin = exports.EvalDevToolModulePlugin = exports.EnvironmentPlugin = exports.EntryPlugin = exports.EntryOptionPlugin = exports.DynamicEntryPlugin = exports.DllReferencePlugin = exports.DllPlugin = exports.Dependency = exports.DefinePlugin = exports.ContextReplacementPlugin = exports.Compiler = exports.Compilation = exports.BannerPlugin = exports.AsyncDependenciesBlock = exports.experiments = exports.sources = exports.util = exports.sharing = exports.container = exports.library = exports.wasm = exports.electron = exports.node = exports.webworker = exports.web = exports.optimize = exports.javascript = exports.config = exports.ValidationError = exports.RuntimeGlobals = exports.ModuleFilenameHelpers = exports.version = exports.rspack = exports.RspackDynamicDll = exports.RspackChain = void 0;
|
|
4
|
+
exports.MultiStats = exports.WebpackOptionsApply = exports.WebpackError = exports.Template = exports.Stats = exports.SourceMapDevToolPlugin = exports.SingleEntryPlugin = void 0;
|
|
5
5
|
const core_1 = require("@rspack/core");
|
|
6
6
|
Object.defineProperty(exports, "rspack", { enumerable: true, get: function () { return core_1.rspack; } });
|
|
7
7
|
var base_rspack_1 = require("./config/base.rspack");
|
|
8
8
|
Object.defineProperty(exports, "RspackChain", { enumerable: true, get: function () { return base_rspack_1.RspackChain; } });
|
|
9
|
+
var rspack_dynamic_dll_1 = require("./dynamic-dll/rspack-dynamic-dll");
|
|
10
|
+
Object.defineProperty(exports, "RspackDynamicDll", { enumerable: true, get: function () { return rspack_dynamic_dll_1.RspackDynamicDll; } });
|
|
9
11
|
/**
|
|
10
12
|
* re-export for shuvi plugin
|
|
11
13
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shuvi/toolpack",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.14",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/shuvijs/shuvi.git",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@rspack/cli": "^1.4.2",
|
|
37
37
|
"@rspack/core": "^1.4.2",
|
|
38
38
|
"@rspack/plugin-html": "^0.5.8",
|
|
39
|
-
"@shuvi/compiler": "2.0.0-dev.
|
|
40
|
-
"@shuvi/shared": "2.0.0-dev.
|
|
41
|
-
"@shuvi/utils": "2.0.0-dev.
|
|
39
|
+
"@shuvi/compiler": "2.0.0-dev.14",
|
|
40
|
+
"@shuvi/shared": "2.0.0-dev.14",
|
|
41
|
+
"@shuvi/utils": "2.0.0-dev.14",
|
|
42
42
|
"@swc/helpers": "0.4.3",
|
|
43
43
|
"babel-loader": "8.2.2",
|
|
44
44
|
"babel-plugin-syntax-jsx": "6.18.0",
|