weboptimizer 4.0.45 → 4.0.47
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 +2 -2
- package/webpackConfigurator.js +77 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weboptimizer",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.47",
|
|
4
4
|
"description": "A generic web optimizer, (module) bundler and development environment.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webpack",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"babel-loader": "^10.1.1",
|
|
95
95
|
"babel-plugin-polyfill-corejs3": "^1.0.0",
|
|
96
96
|
"babel-plugin-transform-rewrite-imports": "^1.5.4",
|
|
97
|
-
"clientnode": "4.0.
|
|
97
|
+
"clientnode": "4.0.1480",
|
|
98
98
|
"core-js": "^3.49.0",
|
|
99
99
|
"ejs": "^6.0.1",
|
|
100
100
|
"exports-loader": "^5.0.0",
|
package/webpackConfigurator.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
// region imports
|
|
19
19
|
import { convertToValidVariableName, evaluate, escapeRegularExpressions, extend, importFilesystemAPI, isFileSync, isObject, isPlainObject, Logger, mask, optionalImport, represent, UTILITY_SCOPE } from 'clientnode';
|
|
20
|
+
import { rm, writeFile } from 'fs/promises';
|
|
20
21
|
import { extname, join, relative, resolve } from 'path';
|
|
21
22
|
import util from 'util';
|
|
22
23
|
import webpack from 'webpack';
|
|
@@ -762,6 +763,70 @@ if (configuration.path.configuration.json) try {
|
|
|
762
763
|
} catch {
|
|
763
764
|
log.debug('Optional configuration file', `"${configuration.path.configuration.json}" not available.`);
|
|
764
765
|
}
|
|
766
|
+
|
|
767
|
+
// region collapse multi-module ("multi-main") entry chunks into a barrel
|
|
768
|
+
/*
|
|
769
|
+
NOTE: Webpack awaits a single (async) entry module before completing the
|
|
770
|
+
startup evaluation but does NOT await async modules that are referenced as
|
|
771
|
+
part of an array ("multi-main") entry chunk. As a consequence any
|
|
772
|
+
top-level "await" (async module) reachable from such an entry - and every
|
|
773
|
+
side effect depending on it, like jest's synchronous "describe" / "test"
|
|
774
|
+
registration - would run only after the synchronous startup phase already
|
|
775
|
+
finished (leading for example to "Cannot nest a describe inside a test").
|
|
776
|
+
|
|
777
|
+
To keep evaluation deterministic we collapse every chunk which bundles
|
|
778
|
+
more than one module into a single generated barrel module which webpack
|
|
779
|
+
treats (and, when async, awaits) as a whole. Re-exporting via "export *"
|
|
780
|
+
preserves the (named) exports of all bundled modules while guaranteeing
|
|
781
|
+
each one is evaluated in the given order.
|
|
782
|
+
|
|
783
|
+
NOTE: The barrel and every module it bundles are additionally forced to
|
|
784
|
+
"sideEffects: true" (see the corresponding module rule below). Otherwise a
|
|
785
|
+
consuming package declaring "sideEffects": false (e.g. a test bundle whose
|
|
786
|
+
exports are never imported) would have its bundled modules tree-shaken away
|
|
787
|
+
completely - resulting for example in a test bundle without any registered
|
|
788
|
+
test.
|
|
789
|
+
*/
|
|
790
|
+
const generatedBarrelModuleFilePaths = [];
|
|
791
|
+
const barrelledModuleFilePaths = [];
|
|
792
|
+
const normalizedEntryInjection = {};
|
|
793
|
+
for (const [chunkName, moduleIDs] of Object.entries(configuration.injection.entry.normalized)) if (Array.isArray(moduleIDs) && moduleIDs.length > 1) {
|
|
794
|
+
const barrelModuleFilePath = resolve(configuration.path.context, `.__${convertToValidVariableName(chunkName)}__.barrel.mjs`);
|
|
795
|
+
await writeFile(barrelModuleFilePath, moduleIDs.map(moduleID => {
|
|
796
|
+
const specifier = stripLoader(moduleID);
|
|
797
|
+
const relativeOrAbsoluteSpecifier = specifier.startsWith('.') || specifier.startsWith('/') ? specifier : `./${specifier}`;
|
|
798
|
+
barrelledModuleFilePaths.push(resolve(configuration.path.context, relativeOrAbsoluteSpecifier));
|
|
799
|
+
return `export * from ${JSON.stringify(relativeOrAbsoluteSpecifier)}`;
|
|
800
|
+
}).join('\n') + '\n', {
|
|
801
|
+
encoding: configuration.encoding
|
|
802
|
+
});
|
|
803
|
+
generatedBarrelModuleFilePaths.push(barrelModuleFilePath);
|
|
804
|
+
normalizedEntryInjection[chunkName] = [`./${relative(configuration.path.context, barrelModuleFilePath)}`];
|
|
805
|
+
} else normalizedEntryInjection[chunkName] = moduleIDs;
|
|
806
|
+
if (generatedBarrelModuleFilePaths.length) pluginInstances.push({
|
|
807
|
+
apply: compiler => {
|
|
808
|
+
const removeGeneratedBarrelModules = async () => {
|
|
809
|
+
for (const filePath of generatedBarrelModuleFilePaths) try {
|
|
810
|
+
await rm(filePath, {
|
|
811
|
+
force: true
|
|
812
|
+
});
|
|
813
|
+
} catch (error) {
|
|
814
|
+
log.debug('Removing generated barrel entry module ' + `"${filePath}" failed:`, represent(error));
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
/*
|
|
819
|
+
NOTE: "shutdown" is an "AsyncSeriesHook" fired (and awaited) by
|
|
820
|
+
"compiler.close()" for both one-shot builds and watch-mode
|
|
821
|
+
teardown, so "tapPromise" guarantees the (async) removal finishes
|
|
822
|
+
before the process exits. "watchClose" would be a "SyncHook" which
|
|
823
|
+
cannot await a promise-based cleanup, hence it is not used here.
|
|
824
|
+
*/
|
|
825
|
+
compiler.hooks.shutdown.tapPromise('WebOptimizerRemoveGeneratedBarrelModules', removeGeneratedBarrelModules);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
// endregion
|
|
829
|
+
|
|
765
830
|
export let webpackConfiguration = extend(true, {
|
|
766
831
|
bail: !configuration.givenCommandLineArguments.includes('--watch'),
|
|
767
832
|
context: configuration.path.context,
|
|
@@ -791,7 +856,7 @@ export let webpackConfiguration = extend(true, {
|
|
|
791
856
|
typescript: false
|
|
792
857
|
},
|
|
793
858
|
// region input
|
|
794
|
-
entry:
|
|
859
|
+
entry: normalizedEntryInjection,
|
|
795
860
|
externals: configuration.injection.external.modules,
|
|
796
861
|
resolve: {
|
|
797
862
|
alias: module.aliases,
|
|
@@ -843,7 +908,17 @@ export let webpackConfiguration = extend(true, {
|
|
|
843
908
|
// endregion
|
|
844
909
|
mode: configuration.debug ? 'development' : 'production',
|
|
845
910
|
module: {
|
|
846
|
-
rules:
|
|
911
|
+
rules:
|
|
912
|
+
/*
|
|
913
|
+
Force the generated barrel entry modules and every module
|
|
914
|
+
they bundle to be treated as side-effectful so a consuming
|
|
915
|
+
package declaring "sideEffects": false does not get them
|
|
916
|
+
tree-shaken away (which would empty e.g. a test bundle).
|
|
917
|
+
*/
|
|
918
|
+
(generatedBarrelModuleFilePaths.length ? [{
|
|
919
|
+
include: generatedBarrelModuleFilePaths.concat(barrelledModuleFilePaths),
|
|
920
|
+
sideEffects: true
|
|
921
|
+
}] : []).concat(module.additional.pre.map(evaluateAdditionalLoaderConfiguration)).concat(loader.ejs, loader.script, loader.html.main, loader.html.ejs, loader.html.html, loader.style, loader.font.eot, loader.font.svg, loader.font.ttf, loader.font.woff, loader.image, loader.data, module.additional.post.map(evaluateAdditionalLoaderConfiguration))
|
|
847
922
|
},
|
|
848
923
|
node: configuration.nodeEnvironment,
|
|
849
924
|
optimization: {
|