Skip to content

Commit 4866b0d

Browse files
feat: added new optimization.entryIife option
2 parents d90f692 + 14d8fa8 commit 4866b0d

36 files changed

+252
-104
lines changed

declarations/WebpackOptions.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,10 @@ export interface NodeOptions {
16961696
* Enables/Disables integrated optimizations.
16971697
*/
16981698
export interface Optimization {
1699+
/**
1700+
* Avoid wrapping the entry module in an IIFE.
1701+
*/
1702+
avoidEntryIife?: boolean;
16991703
/**
17001704
* Check for incompatible wasm types when importing/exporting from/to ESM.
17011705
*/

lib/config/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,7 @@ const applyOptimizationDefaults = (
14341434
D(optimization, "innerGraph", production);
14351435
D(optimization, "mangleExports", production);
14361436
D(optimization, "concatenateModules", production);
1437+
D(optimization, "avoidEntryIife", production);
14371438
D(optimization, "runtimeChunk", false);
14381439
D(optimization, "emitOnErrors", !production);
14391440
D(optimization, "checkWasmTypes", production);

lib/javascript/JavascriptModulesPlugin.js

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -839,18 +839,25 @@ class JavascriptModulesPlugin {
839839
startupSource.add(`var ${RuntimeGlobals.exports} = {};\n`);
840840
}
841841

842-
const renamedInlinedModule = this.renameInlineModule(
843-
allModules,
844-
renderContext,
845-
inlinedModules,
846-
chunkRenderContext,
847-
hooks
848-
);
842+
const avoidEntryIife = compilation.options.optimization.avoidEntryIife;
843+
/** @type {Map<Module, Source> | false} */
844+
let renamedInlinedModule = false;
845+
if (avoidEntryIife) {
846+
renamedInlinedModule = this.getRenamedInlineModule(
847+
allModules,
848+
renderContext,
849+
inlinedModules,
850+
chunkRenderContext,
851+
hooks,
852+
allStrict,
853+
Boolean(chunkModules)
854+
);
855+
}
849856

850857
for (const m of inlinedModules) {
851-
const renderedModule =
852-
renamedInlinedModule.get(m) ||
853-
this.renderModule(m, chunkRenderContext, hooks, false);
858+
const renderedModule = renamedInlinedModule
859+
? renamedInlinedModule.get(m)
860+
: this.renderModule(m, chunkRenderContext, hooks, false);
854861

855862
if (renderedModule) {
856863
const innerStrict =
@@ -868,9 +875,11 @@ class JavascriptModulesPlugin {
868875
? // TODO check globals and top-level declarations of other entries and chunk modules
869876
// to make a better decision
870877
"it need to be isolated against other entry modules."
871-
: exports && !webpackExports
872-
? `it uses a non-standard name for the exports (${m.exportsArgument}).`
873-
: hooks.embedInRuntimeBailout.call(m, renderContext);
878+
: chunkModules && !renamedInlinedModule
879+
? "it need to be isolated against other modules in the chunk."
880+
: exports && !webpackExports
881+
? `it uses a non-standard name for the exports (${m.exportsArgument}).`
882+
: hooks.embedInRuntimeBailout.call(m, renderContext);
874883
let footer;
875884
if (iife !== undefined) {
876885
startupSource.add(
@@ -1420,25 +1429,41 @@ class JavascriptModulesPlugin {
14201429
* @param {Set<Module>} inlinedModules inlinedModules
14211430
* @param {ChunkRenderContext} chunkRenderContext chunkRenderContext
14221431
* @param {CompilationHooks} hooks hooks
1423-
* @returns {Map<Module, Source>} renamed inlined modules
1432+
* @param {boolean} allStrict allStrict
1433+
* @param {boolean} hasChunkModules hasChunkModules
1434+
* @returns {Map<Module, Source> | false} renamed inlined modules
14241435
*/
1425-
renameInlineModule(
1436+
getRenamedInlineModule(
14261437
allModules,
14271438
renderContext,
14281439
inlinedModules,
14291440
chunkRenderContext,
1430-
hooks
1441+
hooks,
1442+
allStrict,
1443+
hasChunkModules
14311444
) {
1445+
const innerStrict = !allStrict && allModules.every(m => m.buildInfo.strict);
1446+
const isMultipleEntries = inlinedModules.size > 1;
1447+
const singleEntryWithModules = inlinedModules.size === 1 && hasChunkModules;
1448+
1449+
// TODO:
1450+
// This step is before the IIFE reason calculation. Ideally, it should only be executed when this function can optimize the
1451+
// IIFE reason. Otherwise, it should directly return false. There are four reasons now, we have skipped two already, the left
1452+
// one is 'it uses a non-standard name for the exports'.
1453+
if (isMultipleEntries || innerStrict || !singleEntryWithModules) {
1454+
return false;
1455+
}
1456+
1457+
/** @type {Map<Module, Source>} */
1458+
const renamedInlinedModules = new Map();
14321459
const { runtimeTemplate } = renderContext;
14331460

14341461
/** @typedef {{ source: Source, ast: any, variables: Set<Variable>, usedInNonInlined: Set<Variable>}} InlinedModulesInfo */
1435-
14361462
/** @type {Map<Module, InlinedModulesInfo>} */
14371463
const inlinedModulesToInfo = new Map();
14381464
/** @type {Set<string>} */
14391465
const nonInlinedModuleThroughIdentifiers = new Set();
14401466
/** @type {Map<Module, Source>} */
1441-
const renamedInlinedModules = new Map();
14421467

14431468
for (const m of allModules) {
14441469
const isInlinedModule = inlinedModules && inlinedModules.has(m);

schemas/WebpackOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemas/WebpackOptions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2580,6 +2580,10 @@
25802580
"type": "object",
25812581
"additionalProperties": false,
25822582
"properties": {
2583+
"avoidEntryIife": {
2584+
"description": "Avoid wrapping the entry module in an IIFE.",
2585+
"type": "boolean"
2586+
},
25832587
"checkWasmTypes": {
25842588
"description": "Check for incompatible wasm types when importing/exporting from/to ESM.",
25852589
"type": "boolean"

test/Defaults.unittest.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ describe("snapshots", () => {
265265
"global": true,
266266
},
267267
"optimization": Object {
268+
"avoidEntryIife": false,
268269
"checkWasmTypes": false,
269270
"chunkIds": "natural",
270271
"concatenateModules": false,
@@ -697,13 +698,15 @@ describe("snapshots", () => {
697698
- "mode": "none",
698699
+ "mode": undefined,
699700
@@ ... @@
701+
- "avoidEntryIife": false,
700702
- "checkWasmTypes": false,
701703
- "chunkIds": "natural",
702704
- "concatenateModules": false,
703705
- "emitOnErrors": true,
704706
- "flagIncludedChunks": false,
705707
- "innerGraph": false,
706708
- "mangleExports": false,
709+
+ "avoidEntryIife": true,
707710
+ "checkWasmTypes": true,
708711
+ "chunkIds": "deterministic",
709712
+ "concatenateModules": true,
@@ -764,13 +767,15 @@ describe("snapshots", () => {
764767
- "mode": "none",
765768
+ "mode": "production",
766769
@@ ... @@
770+
- "avoidEntryIife": false,
767771
- "checkWasmTypes": false,
768772
- "chunkIds": "natural",
769773
- "concatenateModules": false,
770774
- "emitOnErrors": true,
771775
- "flagIncludedChunks": false,
772776
- "innerGraph": false,
773777
- "mangleExports": false,
778+
+ "avoidEntryIife": true,
774779
+ "checkWasmTypes": true,
775780
+ "chunkIds": "deterministic",
776781
+ "concatenateModules": true,

test/Validation.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ describe("Validation", () => {
618618
expect(msg).toMatchInlineSnapshot(`
619619
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
620620
- configuration.optimization has an unknown property 'hashedModuleIds'. These properties are valid:
621-
object { checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
621+
object { avoidEntryIife?, checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
622622
-> Enables/Disables integrated optimizations.
623623
Did you mean optimization.moduleIds: \\"hashed\\" (BREAKING CHANGE since webpack 5)?"
624624
`)
@@ -634,7 +634,7 @@ describe("Validation", () => {
634634
expect(msg).toMatchInlineSnapshot(`
635635
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
636636
- configuration.optimization has an unknown property 'namedChunks'. These properties are valid:
637-
object { checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
637+
object { avoidEntryIife?, checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
638638
-> Enables/Disables integrated optimizations.
639639
Did you mean optimization.chunkIds: \\"named\\" (BREAKING CHANGE since webpack 5)?"
640640
`)
@@ -650,7 +650,7 @@ describe("Validation", () => {
650650
expect(msg).toMatchInlineSnapshot(`
651651
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
652652
- configuration.optimization has an unknown property 'occurrenceOrder'. These properties are valid:
653-
object { checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
653+
object { avoidEntryIife?, checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
654654
-> Enables/Disables integrated optimizations.
655655
Did you mean optimization.chunkIds: \\"size\\" and optimization.moduleIds: \\"size\\" (BREAKING CHANGE since webpack 5)?"
656656
`)

test/__snapshots__/Cli.basictest.js.snap

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5230,6 +5230,19 @@ Object {
52305230
"multiple": false,
52315231
"simpleType": "string",
52325232
},
5233+
"optimization-avoid-entry-iife": Object {
5234+
"configs": Array [
5235+
Object {
5236+
"description": "Avoid wrapping the entry module in an IIFE.",
5237+
"multiple": false,
5238+
"path": "optimization.avoidEntryIife",
5239+
"type": "boolean",
5240+
},
5241+
],
5242+
"description": "Avoid wrapping the entry module in an IIFE.",
5243+
"multiple": false,
5244+
"simpleType": "boolean",
5245+
},
52335246
"optimization-check-wasm-types": Object {
52345247
"configs": Array [
52355248
Object {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { value as value1 } from './module1'
2+
const value2 = require('./module2')
3+
const value3 = require('./module3')
4+
5+
let value = 42
6+
let src_value = 43
7+
let src_src_value = 44
8+
9+
it('inlined module should not leak to non-inlined modules', () => {
10+
expect(value1).toBe(undefined)
11+
expect(value).toBe(42)
12+
expect(src_value).toBe(43)
13+
expect(src_src_value).toBe(44)
14+
expect(value2).toBe("undefined") // should not touch leaked `value` variable
15+
expect(value3).toBe("undefined") // should not touch leaked `src_value` variable
16+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
let value
2+
3+
export { value }

0 commit comments

Comments
 (0)