@shuvi/toolpack 1.0.62 → 2.0.0-dev.6
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/utils/tsCheckerRspackPlugin.d.ts +4 -0
- package/lib/utils/tsCheckerRspackPlugin.js +7 -0
- package/lib/webpack/config/base.rspack.d.ts +35 -0
- package/lib/webpack/config/base.rspack.js +329 -0
- package/lib/webpack/config/browser.rspack.d.ts +3 -0
- package/lib/webpack/config/browser.rspack.js +228 -0
- package/lib/webpack/config/{browser.d.ts → browser.webpack.d.ts} +1 -1
- package/lib/webpack/config/{browser.js → browser.webpack.js} +11 -11
- package/lib/webpack/config/index.d.ts +4 -4
- package/lib/webpack/config/index.js +4 -4
- package/lib/webpack/config/index.webpack.d.ts +4 -0
- package/lib/webpack/config/index.webpack.js +20 -0
- package/lib/webpack/config/node.rspack.d.ts +2 -0
- package/lib/webpack/config/node.rspack.js +52 -0
- package/lib/webpack/config/{node.d.ts → node.webpack.d.ts} +1 -1
- package/lib/webpack/config/{node.js → node.webpack.js} +7 -7
- package/lib/webpack/config/parts/external.d.ts +1 -1
- package/lib/webpack/config/parts/helpers.rspack.d.ts +49 -0
- package/lib/webpack/config/parts/helpers.rspack.js +106 -0
- package/lib/webpack/config/parts/{helpers.d.ts → helpers.webpack.d.ts} +2 -2
- package/lib/webpack/config/parts/{style.d.ts → style.rspack.d.ts} +1 -1
- package/lib/webpack/config/parts/style.rspack.js +294 -0
- package/lib/webpack/config/parts/style.webpack.d.ts +12 -0
- package/lib/webpack/config/parts/{style.js → style.webpack.js} +2 -2
- package/lib/webpack/dynamic-dll/dep/getModuleExports.d.ts +1 -1
- package/lib/webpack/index.d.ts +5 -3
- package/lib/webpack/index.js +13 -8
- package/lib/webpack/index.webpack.d.ts +5 -0
- package/lib/webpack/index.webpack.js +29 -0
- package/lib/webpack/plugins/chunk-names-plugin.d.ts +3 -0
- package/lib/webpack/plugins/chunk-names-plugin.js +3 -0
- package/lib/webpack/plugins/copy-file-plugin.rspack.d.ts +14 -0
- package/lib/webpack/plugins/copy-file-plugin.rspack.js +88 -0
- package/lib/webpack/plugins/module-replace-plugin/dynamic-loader-options-hack.d.ts +8 -0
- package/lib/webpack/plugins/module-replace-plugin/dynamic-loader-options-hack.js +16 -0
- package/lib/webpack/plugins/module-replace-plugin/plugin.rspack.d.ts +38 -0
- package/lib/webpack/plugins/module-replace-plugin/plugin.rspack.js +224 -0
- package/lib/webpack/plugins/module-replace-plugin/stub-loader.d.ts +26 -0
- package/lib/webpack/plugins/module-replace-plugin/stub-loader.js +47 -1
- package/lib/webpack/plugins/module-replace-plugin/stub-loader.rspack.d.ts +31 -0
- package/lib/webpack/plugins/module-replace-plugin/stub-loader.rspack.js +89 -0
- package/lib/webpack/plugins/require-cache-hot-reloader-plugin.rspack.d.ts +13 -0
- package/lib/webpack/plugins/require-cache-hot-reloader-plugin.rspack.js +47 -0
- package/lib/webpack/rspack.d.ts +197 -0
- package/lib/webpack/rspack.js +192 -0
- package/lib/webpack/types.rspack.d.ts +45 -0
- package/lib/webpack/{types.d.ts → types.webpack.d.ts} +1 -1
- package/lib/webpack/types.webpack.js +2 -0
- package/lib/webpack/webpack.d.ts +1 -1
- package/lib/webpack/webpack.js +2 -2
- package/package.json +10 -5
- /package/lib/webpack/config/{base.d.ts → base.webpack.d.ts} +0 -0
- /package/lib/webpack/config/{base.js → base.webpack.js} +0 -0
- /package/lib/webpack/config/parts/{helpers.js → helpers.webpack.js} +0 -0
- /package/lib/webpack/{types.js → types.rspack.js} +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const dynamic_loader_options_hack_1 = __importDefault(require("./dynamic-loader-options-hack"));
|
|
7
|
+
const REPLACED = Symbol('replaced');
|
|
8
|
+
const stubLoader = require.resolve('./stub-loader.rspack');
|
|
9
|
+
const pitchLoader = stubLoader;
|
|
10
|
+
const toString = Object.prototype.toString;
|
|
11
|
+
function isRegExp(target) {
|
|
12
|
+
return toString.call(target) === `[object RegExp]`;
|
|
13
|
+
}
|
|
14
|
+
function isFunction(target) {
|
|
15
|
+
return toString.call(target) === `[object Function]`;
|
|
16
|
+
}
|
|
17
|
+
function getModuleId(wpModule) {
|
|
18
|
+
var _a;
|
|
19
|
+
return (wpModule.rawRequest ||
|
|
20
|
+
((_a = wpModule === null || wpModule === void 0 ? void 0 : wpModule.createData) === null || _a === void 0 ? void 0 : _a.rawRequest) ||
|
|
21
|
+
/* rspack support */
|
|
22
|
+
wpModule.request);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* extract the path, query and fragment from the resource path
|
|
26
|
+
*
|
|
27
|
+
* @param resource - the resource path with query and fragment
|
|
28
|
+
* @returns the path, query and fragment
|
|
29
|
+
*/
|
|
30
|
+
function extractResourceResolveData(resource) {
|
|
31
|
+
const [pathWithQuery, fragment] = resource.split('#');
|
|
32
|
+
const [path, query] = pathWithQuery.split('?');
|
|
33
|
+
return {
|
|
34
|
+
path,
|
|
35
|
+
query: query ? `?${query}` : '',
|
|
36
|
+
fragment
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function isPitcher(loader) {
|
|
40
|
+
return loader.loader.startsWith(pitchLoader);
|
|
41
|
+
}
|
|
42
|
+
function findReplacedModule(configs, query) {
|
|
43
|
+
for (let index = 0; index < configs.length; index++) {
|
|
44
|
+
const { resourceQuery, module } = configs[index];
|
|
45
|
+
let isMatch = false;
|
|
46
|
+
if (isRegExp(resourceQuery)) {
|
|
47
|
+
isMatch = resourceQuery.test(query);
|
|
48
|
+
}
|
|
49
|
+
else if (isFunction(resourceQuery)) {
|
|
50
|
+
isMatch = resourceQuery(query);
|
|
51
|
+
}
|
|
52
|
+
if (isMatch) {
|
|
53
|
+
return module;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const ModuleAction = {
|
|
59
|
+
REPLACE: 'replace',
|
|
60
|
+
RESTORE: 'restore'
|
|
61
|
+
};
|
|
62
|
+
// const knownModules = new Map<string, ModuleInfo>();
|
|
63
|
+
const moduleHandler = new Map();
|
|
64
|
+
const compilerInfo = new Map();
|
|
65
|
+
function getKnownModules(id) {
|
|
66
|
+
const res = [];
|
|
67
|
+
for (const compiler of compilerInfo.values()) {
|
|
68
|
+
const module = compiler.modules.get(id);
|
|
69
|
+
if (module) {
|
|
70
|
+
res.push(module);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
// function forEachModule(id: string, cb: (mod: ModuleInfo) => void) {
|
|
76
|
+
// for (const compiler of compilerInfo.values()) {
|
|
77
|
+
// const mod = compiler.modules.get(id);
|
|
78
|
+
// if (mod) {
|
|
79
|
+
// cb(mod);
|
|
80
|
+
// }
|
|
81
|
+
// }
|
|
82
|
+
// }
|
|
83
|
+
class ModuleReplacePlugin {
|
|
84
|
+
static restoreModule(id) {
|
|
85
|
+
const moduleInfos = getKnownModules(id).filter(m => m.action === ModuleAction.REPLACE);
|
|
86
|
+
if (moduleInfos.length < 1) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const handler = {
|
|
90
|
+
resolve: null,
|
|
91
|
+
pending: new Map()
|
|
92
|
+
};
|
|
93
|
+
moduleHandler.set(id, handler);
|
|
94
|
+
moduleInfos.forEach(moduleInfo => {
|
|
95
|
+
moduleInfo.action = ModuleAction.RESTORE;
|
|
96
|
+
handler.pending.set(moduleInfo.compiler, false);
|
|
97
|
+
});
|
|
98
|
+
return new Promise(resolve => {
|
|
99
|
+
handler.resolve = resolve;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
static replaceModule(id) {
|
|
103
|
+
const moduleInfos = getKnownModules(id);
|
|
104
|
+
if (moduleInfos.length < 1) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
moduleInfos.forEach(moduleInfo => {
|
|
108
|
+
moduleInfo.action = ModuleAction.REPLACE;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
constructor(options) {
|
|
112
|
+
this._options = Object.assign({ modules: [] }, options);
|
|
113
|
+
}
|
|
114
|
+
apply(compiler) {
|
|
115
|
+
var _a;
|
|
116
|
+
const { modules } = this._options;
|
|
117
|
+
// init compiler info
|
|
118
|
+
compilerInfo.set(compiler, {
|
|
119
|
+
modules: new Map()
|
|
120
|
+
});
|
|
121
|
+
const pitcher = {
|
|
122
|
+
resourceQuery(query) {
|
|
123
|
+
const find = findReplacedModule(modules, query);
|
|
124
|
+
return !!find;
|
|
125
|
+
},
|
|
126
|
+
use: [
|
|
127
|
+
{
|
|
128
|
+
loader: pitchLoader
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
};
|
|
132
|
+
// replace original rules
|
|
133
|
+
(_a = compiler.options.module.rules) === null || _a === void 0 ? void 0 : _a.unshift(pitcher);
|
|
134
|
+
compiler.hooks.done.tap('done', () => {
|
|
135
|
+
const finished = [];
|
|
136
|
+
for (const [id, handler] of moduleHandler) {
|
|
137
|
+
if (handler.pending.get(compiler)) {
|
|
138
|
+
handler.pending.delete(compiler);
|
|
139
|
+
}
|
|
140
|
+
if (handler.pending.size <= 0) {
|
|
141
|
+
handler.resolve();
|
|
142
|
+
finished.push(id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
for (const id of finished) {
|
|
146
|
+
moduleHandler.delete(id);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
compiler.hooks.beforeCompile.tapAsync('ModuleReplacePlugin', ({ normalModuleFactory }, callback) => {
|
|
150
|
+
normalModuleFactory.hooks.afterResolve.tap('ModuleReplacePlugin', wpModule => {
|
|
151
|
+
this._collectModules(compiler, wpModule);
|
|
152
|
+
});
|
|
153
|
+
callback();
|
|
154
|
+
});
|
|
155
|
+
compiler.hooks.compilation.tap('ModuleReplacePlugin', compilation => {
|
|
156
|
+
compilation.hooks.buildModule.tap('ModuleReplacePlugin', wpModule => this._handleBuildModule(compiler, wpModule));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
_handleBuildModule(compiler, wpModule) {
|
|
160
|
+
const knownModules = compilerInfo.get(compiler).modules;
|
|
161
|
+
const id = getModuleId(wpModule);
|
|
162
|
+
if (!id) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const moduleInfo = knownModules.get(id);
|
|
166
|
+
if (!moduleInfo) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (moduleInfo.action === ModuleAction.RESTORE) {
|
|
170
|
+
const handler = moduleHandler.get(id);
|
|
171
|
+
if (handler) {
|
|
172
|
+
handler.pending.set(compiler, true);
|
|
173
|
+
}
|
|
174
|
+
const pitcher = (wpModule.loaders || []).find(isPitcher);
|
|
175
|
+
if (pitcher) {
|
|
176
|
+
/**
|
|
177
|
+
* rspack can't get the dynamic options from the loader, so we need to use the dynamicLoaderOptionsHack to get the options
|
|
178
|
+
*/
|
|
179
|
+
pitcher.options = {};
|
|
180
|
+
dynamic_loader_options_hack_1.default.setData(wpModule.resourceResolveData.path, {
|
|
181
|
+
action: moduleInfo.action
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (moduleInfo.action === ModuleAction.REPLACE) {
|
|
187
|
+
if (!wpModule.loaders || wpModule.loaders[REPLACED] !== true) {
|
|
188
|
+
wpModule.loaders[REPLACED] = true;
|
|
189
|
+
const pitcher = (wpModule.loaders || []).find(isPitcher);
|
|
190
|
+
if (pitcher) {
|
|
191
|
+
/**
|
|
192
|
+
* rspack can't get the dynamic options from the loader, so we need to use the dynamicLoaderOptionsHack to get the options
|
|
193
|
+
*/
|
|
194
|
+
pitcher.options = {
|
|
195
|
+
replacedModule: moduleInfo.replacedModule
|
|
196
|
+
};
|
|
197
|
+
dynamic_loader_options_hack_1.default.setData(wpModule.resourceResolveData.path, {
|
|
198
|
+
replacedModule: moduleInfo.replacedModule,
|
|
199
|
+
action: moduleInfo.action
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
_collectModules(compiler, wpModule) {
|
|
206
|
+
const knownModules = compilerInfo.get(compiler).modules;
|
|
207
|
+
const id = getModuleId(wpModule);
|
|
208
|
+
const parsedResourceResolveData = extractResourceResolveData(wpModule.request);
|
|
209
|
+
const query = parsedResourceResolveData.query;
|
|
210
|
+
if (knownModules.has(id) || !id) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const replacedModule = findReplacedModule(this._options.modules, query);
|
|
214
|
+
if (replacedModule) {
|
|
215
|
+
knownModules.set(id, {
|
|
216
|
+
action: ModuleAction.REPLACE,
|
|
217
|
+
replacedModule,
|
|
218
|
+
compiler,
|
|
219
|
+
loaders: []
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.default = ModuleReplacePlugin;
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { PitchLoaderDefinitionFunction } from 'webpack';
|
|
2
|
+
/**
|
|
3
|
+
* Options passed to the stub loader from the ModuleReplacePlugin
|
|
4
|
+
*/
|
|
2
5
|
export interface ModuleReplaceOption {
|
|
6
|
+
/** Path of the module to replace the original with */
|
|
3
7
|
replacedModule: string;
|
|
4
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Pitch loader function for module replacement
|
|
11
|
+
*
|
|
12
|
+
* This loader runs in the "pitch" phase of webpack's loader pipeline,
|
|
13
|
+
* which means it executes before the actual module content is processed.
|
|
14
|
+
* It's responsible for:
|
|
15
|
+
* - Intercepting module requests
|
|
16
|
+
* - Replacing the module source with a different module
|
|
17
|
+
* - Maintaining proper exports from the replacement module
|
|
18
|
+
*
|
|
19
|
+
* The pitch function returns a string that becomes the new module source,
|
|
20
|
+
* effectively replacing the original module content.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```javascript
|
|
24
|
+
* // When replacing './api.js' with './mock-api.js'
|
|
25
|
+
* // This loader will generate:
|
|
26
|
+
* // import mod from './mock-api.js';
|
|
27
|
+
* // export * from './mock-api.js';
|
|
28
|
+
* // export default mod;
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
5
31
|
export declare const pitch: PitchLoaderDefinitionFunction<ModuleReplaceOption>;
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pitch = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Filter function to remove the stub loader itself from the loader chain
|
|
6
|
+
* This prevents infinite recursion when the loader processes itself
|
|
7
|
+
*
|
|
8
|
+
* @param l - Loader object to check
|
|
9
|
+
* @returns True if the loader is not the stub loader itself
|
|
10
|
+
*/
|
|
4
11
|
const isSelfLoader = (l) => l.path !== __filename;
|
|
12
|
+
/**
|
|
13
|
+
* Generate a webpack request string from an array of loaders
|
|
14
|
+
* This creates the proper format for webpack's loader resolution
|
|
15
|
+
*
|
|
16
|
+
* @param loaderCtx - Webpack loader context
|
|
17
|
+
* @param loaders - Array of loader configurations
|
|
18
|
+
* @returns Formatted request string for webpack
|
|
19
|
+
*/
|
|
5
20
|
const genRequest = (loaderCtx, loaders) => {
|
|
6
21
|
const loaderStrings = [];
|
|
7
22
|
loaders.forEach(loader => {
|
|
@@ -16,15 +31,46 @@ const genRequest = (loaderCtx, loaders) => {
|
|
|
16
31
|
loaderCtx.resourcePath + loaderCtx.resourceQuery
|
|
17
32
|
].join('!')));
|
|
18
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Pitch loader function for module replacement
|
|
36
|
+
*
|
|
37
|
+
* This loader runs in the "pitch" phase of webpack's loader pipeline,
|
|
38
|
+
* which means it executes before the actual module content is processed.
|
|
39
|
+
* It's responsible for:
|
|
40
|
+
* - Intercepting module requests
|
|
41
|
+
* - Replacing the module source with a different module
|
|
42
|
+
* - Maintaining proper exports from the replacement module
|
|
43
|
+
*
|
|
44
|
+
* The pitch function returns a string that becomes the new module source,
|
|
45
|
+
* effectively replacing the original module content.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```javascript
|
|
49
|
+
* // When replacing './api.js' with './mock-api.js'
|
|
50
|
+
* // This loader will generate:
|
|
51
|
+
* // import mod from './mock-api.js';
|
|
52
|
+
* // export * from './mock-api.js';
|
|
53
|
+
* // export default mod;
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
19
56
|
const pitch = function () {
|
|
57
|
+
// Disable caching to ensure fresh module replacement
|
|
20
58
|
this.cacheable(false);
|
|
59
|
+
// Get the replacement module path from loader options
|
|
21
60
|
const { replacedModule } = this.getOptions() || {};
|
|
22
61
|
let loaders = this.loaders;
|
|
23
|
-
//
|
|
62
|
+
// Remove the stub loader itself from the loader chain
|
|
63
|
+
// This prevents the loader from processing its own output
|
|
24
64
|
loaders = loaders.filter(isSelfLoader);
|
|
65
|
+
// Determine the request path based on whether we're replacing or using original
|
|
25
66
|
const request = replacedModule
|
|
26
67
|
? JSON.stringify(this.utils.contextify(this.context || this.rootContext, replacedModule))
|
|
27
68
|
: genRequest(this, loaders);
|
|
69
|
+
// Generate the new module source
|
|
70
|
+
// This creates a module that:
|
|
71
|
+
// 1. Imports the replacement module as default export
|
|
72
|
+
// 2. Re-exports all named exports from the replacement module
|
|
73
|
+
// 3. Provides the default export from the replacement module
|
|
28
74
|
return `
|
|
29
75
|
import mod from ${request};
|
|
30
76
|
export * from ${request}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PitchLoaderDefinitionFunction } from 'webpack';
|
|
2
|
+
/**
|
|
3
|
+
* Options passed to the stub loader from the ModuleReplacePlugin
|
|
4
|
+
*/
|
|
5
|
+
export interface ModuleReplaceOption {
|
|
6
|
+
/** Path of the module to replace the original with */
|
|
7
|
+
replacedModule: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Pitch loader function for module replacement
|
|
11
|
+
*
|
|
12
|
+
* This loader runs in the "pitch" phase of webpack's loader pipeline,
|
|
13
|
+
* which means it executes before the actual module content is processed.
|
|
14
|
+
* It's responsible for:
|
|
15
|
+
* - Intercepting module requests
|
|
16
|
+
* - Replacing the module source with a different module
|
|
17
|
+
* - Maintaining proper exports from the replacement module
|
|
18
|
+
*
|
|
19
|
+
* The pitch function returns a string that becomes the new module source,
|
|
20
|
+
* effectively replacing the original module content.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```javascript
|
|
24
|
+
* // When replacing './api.js' with './mock-api.js'
|
|
25
|
+
* // This loader will generate:
|
|
26
|
+
* // import mod from './mock-api.js';
|
|
27
|
+
* // export * from './mock-api.js';
|
|
28
|
+
* // export default mod;
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const pitch: PitchLoaderDefinitionFunction<ModuleReplaceOption>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.pitch = void 0;
|
|
7
|
+
const dynamic_loader_options_hack_1 = __importDefault(require("./dynamic-loader-options-hack"));
|
|
8
|
+
/**
|
|
9
|
+
* Filter function to remove the stub loader itself from the loader chain
|
|
10
|
+
* This prevents infinite recursion when the loader processes itself
|
|
11
|
+
*
|
|
12
|
+
* @param l - Loader object to check
|
|
13
|
+
* @returns True if the loader is not the stub loader itself
|
|
14
|
+
*/
|
|
15
|
+
const isSelfLoader = (l) => l.path !== __filename;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a webpack request string from an array of loaders
|
|
18
|
+
* This creates the proper format for webpack's loader resolution
|
|
19
|
+
*
|
|
20
|
+
* @param loaderCtx - Webpack loader context
|
|
21
|
+
* @param loaders - Array of loader configurations
|
|
22
|
+
* @returns Formatted request string for webpack
|
|
23
|
+
*/
|
|
24
|
+
const genRequest = (loaderCtx, loaders) => {
|
|
25
|
+
const loaderStrings = [];
|
|
26
|
+
loaders.forEach(loader => {
|
|
27
|
+
const request = typeof loader === 'string' ? loader : loader.request;
|
|
28
|
+
// loader.request contains both the resolved loader path and its options
|
|
29
|
+
// query (e.g. ??ref-0)
|
|
30
|
+
loaderStrings.push(request);
|
|
31
|
+
});
|
|
32
|
+
return JSON.stringify(loaderCtx.utils.contextify(loaderCtx.context || loaderCtx.rootContext, '-!' +
|
|
33
|
+
[
|
|
34
|
+
...loaderStrings,
|
|
35
|
+
loaderCtx.resourcePath + loaderCtx.resourceQuery
|
|
36
|
+
].join('!')));
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Pitch loader function for module replacement
|
|
40
|
+
*
|
|
41
|
+
* This loader runs in the "pitch" phase of webpack's loader pipeline,
|
|
42
|
+
* which means it executes before the actual module content is processed.
|
|
43
|
+
* It's responsible for:
|
|
44
|
+
* - Intercepting module requests
|
|
45
|
+
* - Replacing the module source with a different module
|
|
46
|
+
* - Maintaining proper exports from the replacement module
|
|
47
|
+
*
|
|
48
|
+
* The pitch function returns a string that becomes the new module source,
|
|
49
|
+
* effectively replacing the original module content.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```javascript
|
|
53
|
+
* // When replacing './api.js' with './mock-api.js'
|
|
54
|
+
* // This loader will generate:
|
|
55
|
+
* // import mod from './mock-api.js';
|
|
56
|
+
* // export * from './mock-api.js';
|
|
57
|
+
* // export default mod;
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
const pitch = function () {
|
|
61
|
+
// Disable caching to ensure fresh module replacement
|
|
62
|
+
this.cacheable(false);
|
|
63
|
+
const dynamicOptions = dynamic_loader_options_hack_1.default.pop(this.resourcePath);
|
|
64
|
+
// Get the replacement module path from loader options
|
|
65
|
+
/**
|
|
66
|
+
* rspack can't get the dynamic options from the loader, so we need to use the dynamicLoaderOptionsHack to get the options
|
|
67
|
+
*/
|
|
68
|
+
// const { replacedModule } = this.getOptions() || {};
|
|
69
|
+
const { replacedModule } = dynamicOptions || {};
|
|
70
|
+
let loaders = this.loaders;
|
|
71
|
+
// Remove the stub loader itself from the loader chain
|
|
72
|
+
// This prevents the loader from processing its own output
|
|
73
|
+
loaders = loaders.filter(isSelfLoader);
|
|
74
|
+
// Determine the request path based on whether we're replacing or using original
|
|
75
|
+
const request = replacedModule
|
|
76
|
+
? JSON.stringify(this.utils.contextify(this.context || this.rootContext, replacedModule))
|
|
77
|
+
: genRequest(this, loaders);
|
|
78
|
+
// Generate the new module source
|
|
79
|
+
// This creates a module that:
|
|
80
|
+
// 1. Imports the replacement module as default export
|
|
81
|
+
// 2. Re-exports all named exports from the replacement module
|
|
82
|
+
// 3. Provides the default export from the replacement module
|
|
83
|
+
return `
|
|
84
|
+
import mod from ${request};
|
|
85
|
+
export * from ${request}
|
|
86
|
+
export default mod;
|
|
87
|
+
`.trim();
|
|
88
|
+
};
|
|
89
|
+
exports.pitch = pitch;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as Rspack from '../../webpack';
|
|
2
|
+
/**
|
|
3
|
+
* Rspack plugin interface
|
|
4
|
+
*/
|
|
5
|
+
interface RspackPlugin {
|
|
6
|
+
apply(compiler: Rspack.Compiler): void;
|
|
7
|
+
}
|
|
8
|
+
export default class RequireCacheHotReloader implements RspackPlugin {
|
|
9
|
+
previousOutputPathsWebpack5: Set<string>;
|
|
10
|
+
currentOutputPathsWebpack5: Set<string>;
|
|
11
|
+
apply(compiler: Rspack.Compiler): void;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ref: https://github.com/vercel/next.js/blob/canary/packages/next/build/webpack/plugins/nextjs-require-cache-hot-reloader.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
function deleteCache(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
delete require.cache[(0, fs_1.realpathSync)(filePath)];
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
if (e.code !== 'ENOENT')
|
|
11
|
+
throw e;
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
delete require.cache[filePath];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const PLUGIN_NAME = 'RequireCacheHotReloader';
|
|
18
|
+
// This plugin flushes require.cache after emitting the files. Providing 'hot reloading' of server files.
|
|
19
|
+
class RequireCacheHotReloader {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.previousOutputPathsWebpack5 = new Set();
|
|
22
|
+
this.currentOutputPathsWebpack5 = new Set();
|
|
23
|
+
}
|
|
24
|
+
apply(compiler) {
|
|
25
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
|
|
26
|
+
compilation.hooks.additionalTreeRuntimeRequirements.tap(PLUGIN_NAME, (_chunk, runtimeRequirements) => {
|
|
27
|
+
// add this so that the server.js would be emitted after every compilation
|
|
28
|
+
// @ts-ignore webpack typing bug
|
|
29
|
+
runtimeRequirements.add(RuntimeGlobals.getFullHash);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
compiler.hooks.assetEmitted.tap(PLUGIN_NAME, (_file, { targetPath }) => {
|
|
33
|
+
this.currentOutputPathsWebpack5.add(targetPath);
|
|
34
|
+
deleteCache(targetPath);
|
|
35
|
+
});
|
|
36
|
+
compiler.hooks.afterEmit.tap(PLUGIN_NAME, compilation => {
|
|
37
|
+
for (const outputPath of this.previousOutputPathsWebpack5) {
|
|
38
|
+
if (!this.currentOutputPathsWebpack5.has(outputPath)) {
|
|
39
|
+
deleteCache(outputPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.previousOutputPathsWebpack5 = new Set(this.currentOutputPathsWebpack5);
|
|
43
|
+
this.currentOutputPathsWebpack5.clear();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.default = RequireCacheHotReloader;
|