@meteorjs/rspack 0.0.10 → 0.0.12
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/package.json +1 -1
- package/plugins/RequireExtenalsPlugin.js +235 -13
- package/rspack.config.js +17 -20
package/package.json
CHANGED
|
@@ -22,12 +22,25 @@ export class RequireExternalsPlugin {
|
|
|
22
22
|
// It can be used to customize how external modules are mapped to file paths
|
|
23
23
|
// If not provided, the default behavior is to map the external module name.
|
|
24
24
|
externalMap = null,
|
|
25
|
+
// Enable global polyfill for module and exports
|
|
26
|
+
// If true, globalThis.module and globalThis.exports will be defined if they don't exist
|
|
27
|
+
enableGlobalPolyfill = true,
|
|
28
|
+
// Check function to determine if an external import should be eager
|
|
29
|
+
// If provided, it will be called with the package name and should return true for eager imports
|
|
30
|
+
// If not provided or returns false, the import will be lazy (default behavior)
|
|
31
|
+
isEagerImport = null,
|
|
32
|
+
// Array of module paths that should always be imported at the end of the file
|
|
33
|
+
// These will be treated as eager imports but will always be placed after all other imports
|
|
34
|
+
lastImports = null,
|
|
25
35
|
} = {}) {
|
|
26
36
|
this.pluginName = 'RequireExternalsPlugin';
|
|
27
37
|
|
|
28
38
|
// Prepare externals
|
|
29
39
|
this._externals = externals;
|
|
30
40
|
this._externalMap = externalMap;
|
|
41
|
+
this._enableGlobalPolyfill = enableGlobalPolyfill;
|
|
42
|
+
this._isEagerImport = isEagerImport;
|
|
43
|
+
this._lastImports = lastImports;
|
|
31
44
|
this._defaultExternalPrefix = 'external ';
|
|
32
45
|
|
|
33
46
|
// Prepare paths
|
|
@@ -130,8 +143,10 @@ export class RequireExternalsPlugin {
|
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
compiler.hooks.done.tap({ name: this.pluginName, stage: -10 }, (stats) => {
|
|
133
|
-
// 1) Ensure globalThis.module / exports block is present
|
|
134
|
-
this.
|
|
146
|
+
// 1) Ensure globalThis.module / exports block is present if enabled
|
|
147
|
+
if (this._enableGlobalPolyfill) {
|
|
148
|
+
this._ensureGlobalThisModule();
|
|
149
|
+
}
|
|
135
150
|
|
|
136
151
|
// 2) Re-load existing requires from disk on every run
|
|
137
152
|
const existing = this._readExistingRequires();
|
|
@@ -163,21 +178,39 @@ export class RequireExternalsPlugin {
|
|
|
163
178
|
// Strip out any now-empty helper functions:
|
|
164
179
|
// function lazyExternalImportsX() {
|
|
165
180
|
// }
|
|
166
|
-
|
|
167
|
-
|
|
181
|
+
// or new format:
|
|
182
|
+
// // (function eagerExternalImportsX() {
|
|
183
|
+
// // })
|
|
184
|
+
// or lastImports format:
|
|
185
|
+
// // (function lastImports() {
|
|
186
|
+
// // })
|
|
187
|
+
const emptyLazyFnRe = /^function\s+lazyExternalImports\d+\s*\(\)\s*{\s*}\s*(\r?\n)?/gm;
|
|
188
|
+
const emptyEagerFnRe = /^\/\/\s*\(function\s+eagerExternalImports\d+\s*\(\)\s*{\s*\n\/\/\s*\}\)\s*(\r?\n)?/gm;
|
|
189
|
+
const emptyLastFnRe = /^\/\/\s*\(function\s+lastImports(?:\d+)?\s*\(\)\s*{\s*\n\/\/\s*\}\)\s*(\r?\n)?/gm;
|
|
190
|
+
content = content.replace(emptyLazyFnRe, '');
|
|
191
|
+
content = content.replace(emptyEagerFnRe, '');
|
|
192
|
+
content = content.replace(emptyLastFnRe, '');
|
|
168
193
|
|
|
169
194
|
// Write the cleaned file back
|
|
170
195
|
fs.writeFileSync(this.filePath, content, 'utf-8');
|
|
171
196
|
|
|
172
197
|
// Re-populate `existing` so the add-diff is accurate
|
|
173
198
|
existing.clear();
|
|
199
|
+
// Check for require statements
|
|
174
200
|
for (const match of content.matchAll(/require\('([^']+)'\)/g)) {
|
|
175
201
|
existing.add(match[1]);
|
|
176
202
|
}
|
|
203
|
+
// Also check for import statements (used in the new format)
|
|
204
|
+
for (const match of content.matchAll(/import\s+'([^']+)'/g)) {
|
|
205
|
+
existing.add(match[1]);
|
|
206
|
+
}
|
|
177
207
|
}
|
|
178
208
|
|
|
179
|
-
// 3) Collect any new externals from this build
|
|
180
|
-
const
|
|
209
|
+
// 3) Collect any new externals from this build and separate into eager, lazy, and last
|
|
210
|
+
const newLazyRequires = [];
|
|
211
|
+
const newEagerRequires = [];
|
|
212
|
+
const newLastRequires = [];
|
|
213
|
+
|
|
181
214
|
for (const module of info.modules) {
|
|
182
215
|
const name = module.name;
|
|
183
216
|
const matchInfo = this._isExternalModule(name);
|
|
@@ -186,19 +219,180 @@ export class RequireExternalsPlugin {
|
|
|
186
219
|
const pkg = this._extractPackageName(name, matchInfo);
|
|
187
220
|
if (pkg && !existing.has(pkg)) {
|
|
188
221
|
existing.add(pkg);
|
|
189
|
-
|
|
222
|
+
|
|
223
|
+
// Check if this should be a last import
|
|
224
|
+
if (this._lastImports && Array.isArray(this._lastImports) && this._lastImports.includes(pkg)) {
|
|
225
|
+
newLastRequires.push(`require('${pkg}')`);
|
|
226
|
+
}
|
|
227
|
+
// Check if this should be an eager import
|
|
228
|
+
else if (this._isEagerImport && typeof this._isEagerImport === 'function' && this._isEagerImport(pkg)) {
|
|
229
|
+
newEagerRequires.push(`require('${pkg}')`);
|
|
230
|
+
} else {
|
|
231
|
+
// Default to lazy import
|
|
232
|
+
newLazyRequires.push(`require('${pkg}')`);
|
|
233
|
+
}
|
|
190
234
|
}
|
|
191
235
|
}
|
|
192
236
|
|
|
193
|
-
// 4) Append new imports if any
|
|
194
|
-
if (
|
|
237
|
+
// 4) Append new lazy imports if any
|
|
238
|
+
if (newLazyRequires.length) {
|
|
195
239
|
const fnName = `lazyExternalImports${this._funcCount++}`;
|
|
196
|
-
const body =
|
|
240
|
+
const body = newLazyRequires.map(req => ` ${req};`).join('\n');
|
|
197
241
|
const fnCode = `\nfunction ${fnName}() {\n${body}\n}\n`;
|
|
198
242
|
try {
|
|
199
243
|
fs.appendFileSync(this.filePath, fnCode);
|
|
200
244
|
} catch (err) {
|
|
201
|
-
console.error(`Failed to append imports to ${this.filePath}:`, err);
|
|
245
|
+
console.error(`Failed to append lazy imports to ${this.filePath}:`, err);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 5) Append new eager imports if any
|
|
250
|
+
if (newEagerRequires.length) {
|
|
251
|
+
const fnName = `eagerExternalImports${this._funcCount++}`;
|
|
252
|
+
// Convert require statements to import statements
|
|
253
|
+
const body = newEagerRequires
|
|
254
|
+
.map(req => {
|
|
255
|
+
// Extract the module path from require('path')
|
|
256
|
+
const modulePath = req.match(/require\('([^']+)'\)/)[1];
|
|
257
|
+
return `import '${modulePath}';`;
|
|
258
|
+
})
|
|
259
|
+
.join('\n');
|
|
260
|
+
// Use comments instead of actual function
|
|
261
|
+
const fnCode = `\n// (function ${fnName}() {\n${body}\n// })\n`;
|
|
262
|
+
try {
|
|
263
|
+
fs.appendFileSync(this.filePath, fnCode);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.error(`Failed to append eager imports to ${this.filePath}:`, err);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 6) Handle lastImports - these should always be at the end of the file
|
|
270
|
+
// First, check if lastImports already exist in the file
|
|
271
|
+
let lastImportsExist = false;
|
|
272
|
+
let lastImportsAtEnd = false;
|
|
273
|
+
let content = '';
|
|
274
|
+
|
|
275
|
+
if (fs.existsSync(this.filePath)) {
|
|
276
|
+
content = fs.readFileSync(this.filePath, 'utf-8');
|
|
277
|
+
|
|
278
|
+
// Check if lastImports exist in the file
|
|
279
|
+
const lastImportsRe = /\/\/\s*\(function\s+lastImports(?:\d+)?\s*\(\)\s*{\s*\n([\s\S]*?)\/\/\s*\}\)/g;
|
|
280
|
+
const match = lastImportsRe.exec(content);
|
|
281
|
+
|
|
282
|
+
if (match) {
|
|
283
|
+
lastImportsExist = true;
|
|
284
|
+
|
|
285
|
+
// Check if lastImports are at the end of the file
|
|
286
|
+
// We'll consider them at the end if there's only whitespace after them
|
|
287
|
+
const afterLastImports = content.substring(match.index + match[0].length);
|
|
288
|
+
if (/^\s*$/.test(afterLastImports)) {
|
|
289
|
+
lastImportsAtEnd = true;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// If lastImports exist but are not at the end, move them to the end
|
|
295
|
+
if (lastImportsExist && !lastImportsAtEnd) {
|
|
296
|
+
// Remove the existing lastImports
|
|
297
|
+
const lastImportsRe = /\/\/\s*\(function\s+lastImports(?:\d+)?\s*\(\)\s*{\s*\n[\s\S]*?\/\/\s*\}\)\s*(\r?\n)?/g;
|
|
298
|
+
content = content.replace(lastImportsRe, '');
|
|
299
|
+
|
|
300
|
+
// Extract the imports from the existing lastImports
|
|
301
|
+
const importRe = /import\s+'([^']+)'/g;
|
|
302
|
+
const existingLastImports = [];
|
|
303
|
+
let match;
|
|
304
|
+
|
|
305
|
+
while ((match = importRe.exec(content)) !== null) {
|
|
306
|
+
if (this._lastImports && Array.isArray(this._lastImports) && this._lastImports.includes(match[1])) {
|
|
307
|
+
existingLastImports.push(`import '${match[1]}';`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Add any new lastImports
|
|
312
|
+
if (this._lastImports && Array.isArray(this._lastImports)) {
|
|
313
|
+
for (const pkg of this._lastImports) {
|
|
314
|
+
if (!existingLastImports.some(imp => imp === `import '${pkg}';`) && existing.has(pkg)) {
|
|
315
|
+
existingLastImports.push(`import '${pkg}';`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Add the lastImports to the end of the file
|
|
321
|
+
if (existingLastImports.length > 0) {
|
|
322
|
+
const body = existingLastImports.join('\n');
|
|
323
|
+
const fnCode = `\n// (function lastImports() {\n${body}\n// })\n`;
|
|
324
|
+
fs.writeFileSync(this.filePath, content + fnCode);
|
|
325
|
+
} else {
|
|
326
|
+
fs.writeFileSync(this.filePath, content);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// If lastImports don't exist, add them if needed
|
|
330
|
+
else if (!lastImportsExist) {
|
|
331
|
+
// Collect all lastImports
|
|
332
|
+
const allLastImports = [];
|
|
333
|
+
|
|
334
|
+
// Add any new lastImports from this build
|
|
335
|
+
if (newLastRequires.length) {
|
|
336
|
+
for (const req of newLastRequires) {
|
|
337
|
+
const modulePath = req.match(/require\('([^']+)'\)/)[1];
|
|
338
|
+
allLastImports.push(`import '${modulePath}';`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Add any existing lastImports from the configuration
|
|
343
|
+
if (this._lastImports && Array.isArray(this._lastImports)) {
|
|
344
|
+
for (const pkg of this._lastImports) {
|
|
345
|
+
if (!allLastImports.some(imp => imp === `import '${pkg}';`) && !existing.has(pkg)) {
|
|
346
|
+
allLastImports.push(`import '${pkg}';`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Add the lastImports to the end of the file
|
|
352
|
+
if (allLastImports.length > 0) {
|
|
353
|
+
const body = allLastImports.join('\n');
|
|
354
|
+
const fnCode = `\n// (function lastImports() {\n${body}\n// })\n`;
|
|
355
|
+
try {
|
|
356
|
+
fs.appendFileSync(this.filePath, fnCode);
|
|
357
|
+
} catch (err) {
|
|
358
|
+
console.error(`Failed to append last imports to ${this.filePath}:`, err);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// If lastImports exist and are already at the end, add any new ones
|
|
363
|
+
else if (lastImportsExist && lastImportsAtEnd && newLastRequires.length) {
|
|
364
|
+
// Extract the existing lastImports
|
|
365
|
+
const lastImportsRe = /\/\/\s*\(function\s+lastImports(?:\d+)?\s*\(\)\s*{\s*\n([\s\S]*?)\/\/\s*\}\)/;
|
|
366
|
+
const match = lastImportsRe.exec(content);
|
|
367
|
+
|
|
368
|
+
if (match) {
|
|
369
|
+
const existingBody = match[1];
|
|
370
|
+
const existingImports = new Set();
|
|
371
|
+
|
|
372
|
+
// Extract the imports from the existing lastImports
|
|
373
|
+
const importRe = /import\s+'([^']+)'/g;
|
|
374
|
+
let importMatch;
|
|
375
|
+
|
|
376
|
+
while ((importMatch = importRe.exec(existingBody)) !== null) {
|
|
377
|
+
existingImports.add(importMatch[1]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Add any new lastImports
|
|
381
|
+
let newBody = existingBody;
|
|
382
|
+
for (const req of newLastRequires) {
|
|
383
|
+
const modulePath = req.match(/require\('([^']+)'\)/)[1];
|
|
384
|
+
if (!existingImports.has(modulePath)) {
|
|
385
|
+
newBody += `import '${modulePath}';\n`;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Replace the existing lastImports with the updated ones
|
|
390
|
+
const updatedContent = content.replace(
|
|
391
|
+
lastImportsRe,
|
|
392
|
+
`// (function lastImports() {\n${newBody}// })`
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
fs.writeFileSync(this.filePath, updatedContent);
|
|
202
396
|
}
|
|
203
397
|
}
|
|
204
398
|
});
|
|
@@ -209,12 +403,33 @@ export class RequireExternalsPlugin {
|
|
|
209
403
|
if (fs.existsSync(this.filePath)) {
|
|
210
404
|
try {
|
|
211
405
|
const content = fs.readFileSync(this.filePath, 'utf-8');
|
|
212
|
-
|
|
406
|
+
// Check for lazy, eager, and last external imports functions
|
|
407
|
+
const lazyFnRe = /function\s+lazyExternalImports(\d+)\s*\(\)/g;
|
|
408
|
+
// Only match the new commented format
|
|
409
|
+
const eagerFnRe = /\/\/\s*\(function\s+eagerExternalImports(\d+)\s*\(\)/g;
|
|
410
|
+
// Match the lastImports format
|
|
411
|
+
const lastFnRe = /\/\/\s*\(function\s+lastImports(\d+)?\s*\(\)/g;
|
|
412
|
+
|
|
213
413
|
let match;
|
|
214
|
-
|
|
414
|
+
// Check lazy imports
|
|
415
|
+
while ((match = lazyFnRe.exec(content)) !== null) {
|
|
215
416
|
const n = parseInt(match[1], 10);
|
|
216
417
|
if (n > max) max = n;
|
|
217
418
|
}
|
|
419
|
+
|
|
420
|
+
// Check eager imports
|
|
421
|
+
while ((match = eagerFnRe.exec(content)) !== null) {
|
|
422
|
+
const n = parseInt(match[1], 10);
|
|
423
|
+
if (n > max) max = n;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Check last imports
|
|
427
|
+
while ((match = lastFnRe.exec(content)) !== null) {
|
|
428
|
+
if (match[1]) {
|
|
429
|
+
const n = parseInt(match[1], 10);
|
|
430
|
+
if (n > max) max = n;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
218
433
|
} catch {
|
|
219
434
|
// ignore read errors
|
|
220
435
|
}
|
|
@@ -251,11 +466,18 @@ export class RequireExternalsPlugin {
|
|
|
251
466
|
const existing = new Set();
|
|
252
467
|
try {
|
|
253
468
|
const content = fs.readFileSync(this.filePath, 'utf-8');
|
|
469
|
+
// Check for require statements
|
|
254
470
|
const requireRegex = /require\('([^']+)'\)/g;
|
|
255
471
|
let match;
|
|
256
472
|
while ((match = requireRegex.exec(content)) !== null) {
|
|
257
473
|
existing.add(match[1]);
|
|
258
474
|
}
|
|
475
|
+
|
|
476
|
+
// Also check for import statements (used in the new format)
|
|
477
|
+
const importRegex = /import\s+'([^']+)'/g;
|
|
478
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
479
|
+
existing.add(match[1]);
|
|
480
|
+
}
|
|
259
481
|
} catch {
|
|
260
482
|
// ignore if file missing or unreadable
|
|
261
483
|
}
|
package/rspack.config.js
CHANGED
|
@@ -43,7 +43,7 @@ function createCacheStrategy(mode) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// SWC loader rule (JSX/JS)
|
|
46
|
-
function createSwcConfig({
|
|
46
|
+
function createSwcConfig({ isTypescriptEnabled, isJsxEnabled, isTsxEnabled, externalHelpers, isDevEnvironment }) {
|
|
47
47
|
const defaultConfig = {
|
|
48
48
|
jsc: {
|
|
49
49
|
baseUrl: process.cwd(),
|
|
@@ -56,8 +56,8 @@ function createSwcConfig({ isRun, isTypescriptEnabled, isJsxEnabled, isTsxEnable
|
|
|
56
56
|
target: 'es2015',
|
|
57
57
|
transform: {
|
|
58
58
|
react: {
|
|
59
|
-
development:
|
|
60
|
-
refresh:
|
|
59
|
+
development: isDevEnvironment,
|
|
60
|
+
refresh: isDevEnvironment,
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
externalHelpers,
|
|
@@ -156,12 +156,13 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
156
156
|
console.log('[i] Meteor flags:', Meteor);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
const isDevEnvironment = isRun && isDev && !isTest;
|
|
159
160
|
const swcConfigRule = createSwcConfig({
|
|
160
|
-
isRun,
|
|
161
161
|
isTypescriptEnabled,
|
|
162
162
|
isJsxEnabled,
|
|
163
163
|
isTsxEnabled,
|
|
164
164
|
externalHelpers: swcExternalHelpers,
|
|
165
|
+
isDevEnvironment,
|
|
165
166
|
});
|
|
166
167
|
const externals = [
|
|
167
168
|
/^meteor.*/,
|
|
@@ -193,16 +194,12 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
193
194
|
filePath: path.join(buildContext, runPath),
|
|
194
195
|
...(Meteor.isBlazeEnabled && {
|
|
195
196
|
externals: /\.html$/,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const relContext = path.relative(process.cwd(), context);
|
|
200
|
-
const { name } = path.parse(request);
|
|
201
|
-
return `./${relContext}/template.${name}.js`;
|
|
202
|
-
}
|
|
203
|
-
return request;
|
|
197
|
+
isEagerImport: (module) => module.endsWith('.html'),
|
|
198
|
+
...isProd && {
|
|
199
|
+
lastImports: [`./${outputFilename}`],
|
|
204
200
|
},
|
|
205
201
|
}),
|
|
202
|
+
enableGlobalPolyfill: isDevEnvironment,
|
|
206
203
|
});
|
|
207
204
|
|
|
208
205
|
const clientNameConfig = `[${isTest && 'test-' || ''}${isTestModule && 'module' || 'client'}-rspack]`;
|
|
@@ -210,12 +207,12 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
210
207
|
let clientConfig = {
|
|
211
208
|
name: clientNameConfig,
|
|
212
209
|
target: 'web',
|
|
213
|
-
mode,
|
|
210
|
+
mode: 'development',
|
|
214
211
|
entry: path.resolve(process.cwd(), buildContext, entryPath),
|
|
215
212
|
output: {
|
|
216
213
|
path: clientOutputDir,
|
|
217
214
|
filename: () =>
|
|
218
|
-
|
|
215
|
+
isDevEnvironment ? outputFilename : `../${buildContext}/${outputPath}`,
|
|
219
216
|
libraryTarget: 'commonjs',
|
|
220
217
|
publicPath: '/',
|
|
221
218
|
chunkFilename: `${bundlesContext}/[id].[chunkhash].js`,
|
|
@@ -243,7 +240,7 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
243
240
|
externals,
|
|
244
241
|
plugins: [
|
|
245
242
|
...[
|
|
246
|
-
...(isReactEnabled && reactRefreshModule
|
|
243
|
+
...(isReactEnabled && reactRefreshModule && isDevEnvironment
|
|
247
244
|
? [new reactRefreshModule()]
|
|
248
245
|
: []),
|
|
249
246
|
requireExternalsPlugin,
|
|
@@ -261,14 +258,14 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
261
258
|
}),
|
|
262
259
|
],
|
|
263
260
|
watchOptions,
|
|
264
|
-
devtool:
|
|
265
|
-
...(
|
|
261
|
+
devtool: isDevEnvironment || isTest ? 'source-map' : 'hidden-source-map',
|
|
262
|
+
...(isDevEnvironment && {
|
|
266
263
|
devServer: {
|
|
267
264
|
static: { directory: clientOutputDir, publicPath: '/__rspack__/' },
|
|
268
265
|
hot: true,
|
|
269
266
|
liveReload: true,
|
|
270
267
|
...(Meteor.isBlazeEnabled && { hot: false }),
|
|
271
|
-
port:
|
|
268
|
+
port: Meteor.devServerPort || 8080,
|
|
272
269
|
devMiddleware: {
|
|
273
270
|
writeToDisk: false,
|
|
274
271
|
},
|
|
@@ -322,8 +319,8 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
322
319
|
isTestModule && requireExternalsPlugin,
|
|
323
320
|
],
|
|
324
321
|
watchOptions,
|
|
325
|
-
devtool:
|
|
326
|
-
...((
|
|
322
|
+
devtool: isDevEnvironment || isTest ? 'source-map' : 'hidden-source-map',
|
|
323
|
+
...((isDevEnvironment || isTest) &&
|
|
327
324
|
createCacheStrategy(mode)
|
|
328
325
|
),
|
|
329
326
|
};
|