diff --git a/lib/FlagAllModulesAsUsedPlugin.js b/lib/FlagAllModulesAsUsedPlugin.js index 0fe64d798f8..c84ed38aaca 100644 --- a/lib/FlagAllModulesAsUsedPlugin.js +++ b/lib/FlagAllModulesAsUsedPlugin.js @@ -40,6 +40,10 @@ class FlagAllModulesAsUsedPlugin { const exportsInfo = moduleGraph.getExportsInfo(module); exportsInfo.setUsedInUnknownWay(runtime); moduleGraph.addExtraReason(module, this.explanation); + if (module.factoryMeta === undefined) { + module.factoryMeta = {}; + } + module.factoryMeta.sideEffectFree = false; } } ); diff --git a/lib/NormalModule.js b/lib/NormalModule.js index 190eb87c37a..1d9339b20a0 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -868,8 +868,10 @@ class NormalModule extends Module { * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only */ getSideEffectsConnectionState(moduleGraph) { - if (this.factoryMeta !== undefined && this.factoryMeta.sideEffectFree) - return false; + if (this.factoryMeta !== undefined) { + if (this.factoryMeta.sideEffectFree) return false; + if (this.factoryMeta.sideEffectFree === false) return true; + } if (this.buildMeta !== undefined && this.buildMeta.sideEffectFree) { if (this._isEvaluatingSideEffects) return ModuleGraphConnection.CIRCULAR_CONNECTION; diff --git a/lib/cache/PackFileCacheStrategy.js b/lib/cache/PackFileCacheStrategy.js index 1b355e38acf..6edc817739a 100644 --- a/lib/cache/PackFileCacheStrategy.js +++ b/lib/cache/PackFileCacheStrategy.js @@ -961,10 +961,8 @@ class PackFileCacheStrategy { for (const dep of this.newBuildDependencies) { if (!this.buildDependencies.has(dep)) { newBuildDependencies.add(dep); - this.buildDependencies.add(dep); } } - this.newBuildDependencies.clear(); if (newBuildDependencies.size > 0 || !this.buildSnapshot) { if (reportProgress) reportProgress(0.5, "resolve build dependencies"); this.logger.debug( @@ -1093,6 +1091,10 @@ class PackFileCacheStrategy { logger: this.logger }) .then(() => { + for (const dep of newBuildDependencies) { + this.buildDependencies.add(dep); + } + this.newBuildDependencies.clear(); this.logger.timeEnd(`store pack`); this.logger.log(`Stored pack`); }) diff --git a/lib/optimize/SideEffectsFlagPlugin.js b/lib/optimize/SideEffectsFlagPlugin.js index ea50291e8e1..675c3ffde5a 100644 --- a/lib/optimize/SideEffectsFlagPlugin.js +++ b/lib/optimize/SideEffectsFlagPlugin.js @@ -82,16 +82,16 @@ class SideEffectsFlagPlugin { resolveData.relativePath ) { const sideEffects = resolveData.descriptionFileData.sideEffects; - const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects( - resolveData.relativePath, - sideEffects, - cache - ); - if (!hasSideEffects) { + if (sideEffects !== undefined) { if (module.factoryMeta === undefined) { module.factoryMeta = {}; } - module.factoryMeta.sideEffectFree = true; + const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects( + resolveData.relativePath, + sideEffects, + cache + ); + module.factoryMeta.sideEffectFree = !hasSideEffects; } } @@ -101,15 +101,11 @@ class SideEffectsFlagPlugin { normalModuleFactory.hooks.module.tap( "SideEffectsFlagPlugin", (module, data) => { - if (data.settings.sideEffects === false) { + if (typeof data.settings.sideEffects === "boolean") { if (module.factoryMeta === undefined) { module.factoryMeta = {}; } - module.factoryMeta.sideEffectFree = true; - } else if (data.settings.sideEffects === true) { - if (module.factoryMeta !== undefined) { - module.factoryMeta.sideEffectFree = false; - } + module.factoryMeta.sideEffectFree = !data.settings.sideEffects; } return module; } diff --git a/package.json b/package.json index 83fab07083b..8e81b9d8bc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webpack", - "version": "5.10.2", + "version": "5.10.3", "author": "Tobias Koppers @sokra", "description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.", "license": "MIT", diff --git a/test/BuildDependencies.test.js b/test/BuildDependencies.test.js index 40558609224..069fc03c631 100644 --- a/test/BuildDependencies.test.js +++ b/test/BuildDependencies.test.js @@ -9,14 +9,15 @@ const cacheDirectory = path.resolve(__dirname, "js/buildDepsCache"); const outputDirectory = path.resolve(__dirname, "js/buildDeps"); const inputDirectory = path.resolve(__dirname, "js/buildDepsInput"); -const exec = n => { +const exec = (n, options = {}) => { return new Promise((resolve, reject) => { const p = child_process.fork( path.resolve(__dirname, "fixtures/buildDependencies/run.js"), - [n], - { stdio: ["ignore", "pipe", "inherit", "ipc"] } + [n, JSON.stringify(options)], + { stdio: ["ignore", "pipe", "pipe", "ipc"] } ); const chunks = []; + p.stderr.on("data", chunk => chunks.push(chunk)); p.stdout.on("data", chunk => chunks.push(chunk)); p.once("exit", code => { const stdout = Buffer.concat(chunks).toString("utf-8"); @@ -45,6 +46,15 @@ describe("BuildDependencies", () => { fs.mkdir(inputDirectory, { recursive: true }, done); }); it("should capture loader and config dependencies", async () => { + fs.writeFileSync( + path.resolve(inputDirectory, "loader-dependency.js"), + "module.exports = 0;" + ); + fs.writeFileSync( + path.resolve(inputDirectory, "config-dependency.js"), + "module.exports = 0;" + ); + await exec("0", { invalidBuildDepdencies: true, buildTwice: true }); fs.writeFileSync( path.resolve(inputDirectory, "loader-dependency.js"), "module.exports = 1;" @@ -53,23 +63,23 @@ describe("BuildDependencies", () => { path.resolve(inputDirectory, "config-dependency.js"), "module.exports = 1;" ); - await exec("0"); + await exec("1"); fs.writeFileSync( path.resolve(inputDirectory, "loader-dependency.js"), "module.exports = Date.now();" ); const now1 = Date.now(); - await exec("1"); await exec("2"); + await exec("3"); fs.writeFileSync( path.resolve(inputDirectory, "config-dependency"), "module.exports = Date.now();" ); const now2 = Date.now(); - await exec("3"); - const now3 = Date.now(); await exec("4"); - const results = Array.from({ length: 5 }).map((_, i) => + const now3 = Date.now(); + await exec("5"); + const results = Array.from({ length: 6 }).map((_, i) => require(`./js/buildDeps/${i}/main.js`) ); for (const r of results) { @@ -77,24 +87,28 @@ describe("BuildDependencies", () => { expect(typeof r.config).toBe("number"); expect(typeof r.uncached).toBe("number"); } - expect(results[0].loader).toBe(1); - expect(results[0].config).toBe(1); - expect(results[0].uncached).toBe(1); - // 0 -> 1 should be invalidated - expect(results[1].loader).toBeGreaterThan(now1); + expect(results[0].loader).toBe(0); + expect(results[0].config).toBe(0); + expect(results[0].uncached).toBe(0); + // 0 -> 1 should not cache at all because of invalid buildDeps + expect(results[1].loader).toBe(1); expect(results[1].config).toBe(1); expect(results[1].uncached).toBe(1); - // 1 -> 2 should stay cached - expect(results[2].loader).toBe(results[1].loader); + // 1 -> 2 should be invalidated + expect(results[2].loader).toBeGreaterThan(now1); expect(results[2].config).toBe(1); expect(results[2].uncached).toBe(1); - // 2 -> 3 should be invalidated - expect(results[3].loader).toBeGreaterThan(now2); - expect(results[3].config).toBeGreaterThan(now2); - expect(results[3].uncached).toBe(results[3].config); - // 3 -> 4 should stay cached, but uncacheable module still rebuilds - expect(results[4].loader).toBe(results[3].loader); - expect(results[4].config).toBe(results[3].config); - expect(results[4].uncached).toBeGreaterThan(now3); + // 2 -> 3 should stay cached + expect(results[3].loader).toBe(results[2].loader); + expect(results[3].config).toBe(1); + expect(results[3].uncached).toBe(1); + // 3 -> 4 should be invalidated + expect(results[4].loader).toBeGreaterThan(now2); + expect(results[4].config).toBeGreaterThan(now2); + expect(results[4].uncached).toBe(results[4].config); + // 4 -> 5 should stay cached, but uncacheable module still rebuilds + expect(results[5].loader).toBe(results[4].loader); + expect(results[5].config).toBe(results[4].config); + expect(results[5].uncached).toBeGreaterThan(now3); }, 100000); }); diff --git a/test/configCases/dll-plugin/0-create-dll/d.js b/test/configCases/dll-plugin/0-create-dll/d.js index 6205d39e382..987d6d7e401 100644 --- a/test/configCases/dll-plugin/0-create-dll/d.js +++ b/test/configCases/dll-plugin/0-create-dll/d.js @@ -1,2 +1 @@ export default "d"; -console.log.bind(console); diff --git a/test/configCases/dll-plugin/2-use-dll-without-scope/index.js b/test/configCases/dll-plugin/2-use-dll-without-scope/index.js index d55ad81ad16..70482da8fdc 100644 --- a/test/configCases/dll-plugin/2-use-dll-without-scope/index.js +++ b/test/configCases/dll-plugin/2-use-dll-without-scope/index.js @@ -4,39 +4,39 @@ import { x2, y1 } from "../0-create-dll/e"; import { B } from "../0-create-dll/h"; import { A } from "../0-create-dll/h1"; -it("should load a module from dll", function() { +it("should load a module from dll", function () { expect(require("../0-create-dll/a")).toBe("a"); }); -it("should load a module of non-default type without extension from dll", function() { +it("should load a module of non-default type without extension from dll", function () { expect(require("../0-create-dll/f")).toBe("f"); }); -it("should load an async module from dll", function(done) { +it("should load an async module from dll", function (done) { require("../0-create-dll/b")() - .then(function(c) { + .then(function (c) { expect(c).toEqual(nsObj({ default: "c" })); done(); }) .catch(done); }); -it("should load an harmony module from dll (default export)", function() { +it("should load an harmony module from dll (default export)", function () { expect(d).toBe("d"); }); -it("should load an harmony module from dll (star export)", function() { +it("should load an harmony module from dll (star export)", function () { expect(x1).toBe(123); expect(x2).toBe(123); expect(y1).toBe(456); expect(y2).toBe(456); }); -it("should load a module with loader applied", function() { +it("should load a module with loader applied", function () { expect(require("../0-create-dll/g.abc.js")).toBe("number"); }); -it("should give modules the correct ids", function() { +it("should give modules the correct ids", function () { expect( Object.keys(__webpack_modules__) .filter(m => !m.startsWith("../..")) @@ -51,16 +51,16 @@ it("should give modules the correct ids", function() { "../0-create-dll/f.jsx", "../0-create-dll/g.abc.js", "../0-create-dll/h.js", - "../0-create-dll/hb.js", + "../0-create-dll/h1.js", "./index.js", "dll-reference ../0-create-dll/dll.js" ]); }); -it("should not crash on side-effect-free modules", function() { +it("should not crash on side-effect-free modules", function () { expect(B).toBe("B"); }); -it("should be able to reference side-effect-free reexport-only module", function() { +it("should be able to reference side-effect-free reexport-only module", function () { expect(A).toBe("A"); }); diff --git a/test/fixtures/buildDependencies/run.js b/test/fixtures/buildDependencies/run.js index 59cdbb47480..1e61e5ed113 100644 --- a/test/fixtures/buildDependencies/run.js +++ b/test/fixtures/buildDependencies/run.js @@ -5,7 +5,9 @@ const value = require("../../js/buildDepsInput/config-dependency"); process.exitCode = 1; -webpack( +const options = JSON.parse(process.argv[3]); + +const compiler = webpack( { mode: "development", context: __dirname, @@ -23,6 +25,9 @@ webpack( ) }) ], + infrastructureLogging: { + level: "verbose" + }, cache: { type: "filesystem", cacheDirectory: path.resolve(__dirname, "../../js/buildDepsCache"), @@ -30,7 +35,8 @@ webpack( config: [ __filename, path.resolve(__dirname, "../../../node_modules/.yarn-integrity") - ] + ], + invalid: options.invalidBuildDepdencies ? ["should-fail-resolving"] : [] } }, snapshot: { @@ -44,7 +50,20 @@ webpack( if (stats.hasErrors()) { return console.log(stats.toString({ all: false, errors: true })); } - process.exitCode = 0; - console.log("OK"); + if (options.buildTwice) { + compiler.run((err, stats) => { + if (err) { + return console.log(err); + } + if (stats.hasErrors()) { + return console.log(stats.toString({ all: false, errors: true })); + } + process.exitCode = 0; + console.log("OK"); + }); + } else { + process.exitCode = 0; + console.log("OK"); + } } ); diff --git a/yarn.lock b/yarn.lock index 887e8e46be6..677be729555 100644 --- a/yarn.lock +++ b/yarn.lock @@ -897,9 +897,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*", "@types/node@^14.14.10": - version "14.14.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" - integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== + version "14.14.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" + integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -2459,9 +2459,9 @@ eslint-plugin-node@^11.0.0: semver "^6.1.0" eslint-plugin-prettier@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz#af391b2226fa0e15c96f36c733f6e9035dbd952c" - integrity sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg== + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.0.tgz#61e295349a65688ffac0b7808ef0a8244bdd8d40" + integrity sha512-tMTwO8iUWlSRZIwS9k7/E4vrTsfvsrcM5p1eftyuqWH25nKsz/o6/54I7jwQ/3zobISyC7wMy9ZsFwgTxOcOpQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -2864,9 +2864,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= fork-ts-checker-webpack-plugin@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.0.5.tgz#20d8766b644833cc5c600b9b7c6fbba0c8087419" - integrity sha512-2jIHv2RhXzSxWtvRQX/ZtOxd5joo+FQYzn+sJ/hyLqApKGgvjMEMF951GnvuSNPheGsqiVzIDjvSZo1qRtry1Q== + version "6.0.7" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.0.7.tgz#90d36489d553c4de0d76b8a75f8734e43c6f8e9d" + integrity sha512-8M9q76SFQFNruI2L5Z/WmDWZsbkzAjEtUcL8QXKSuTLC+8A4MriNNIiXR9yRcKvGxaBTFzygAmUYb3huTiPraw== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -3194,9 +3194,9 @@ human-signals@^1.1.1: integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== husky@^4.2.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.5.tgz#ab8d2a0eb6b62fef2853ee3d442c927d89290902" - integrity sha512-E5S/1HMoDDaqsH8kDF5zeKEQbYqe3wL9zJDyqyYqc8I4vHBtAoxkDBGXox0lZ9RI+k5GyB728vZdmnM4bYap+g== + version "4.3.6" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.6.tgz#ebd9dd8b9324aa851f1587318db4cccb7665a13c" + integrity sha512-o6UjVI8xtlWRL5395iWq9LKDyp/9TE7XMOTvIpEVzW638UcGxTmV5cfel6fsk/jbZSTlvfGVJf2svFtybcIZag== dependencies: chalk "^4.0.0" ci-info "^2.0.0" @@ -4202,9 +4202,9 @@ less-loader@^7.1.0: schema-utils "^3.0.0" less@^3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" - integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== + version "3.13.0" + resolved "https://registry.yarnpkg.com/less/-/less-3.13.0.tgz#6a47bb19d97edcf7a53d444b099275dd6b17c85a" + integrity sha512-uPhr9uoSGVKKYVGz0rXcYBK1zjwcIWRGcbnSgNt66XuIZYrYPaQiS+LeUOvqedBwrwdBYYaLqSff5ytGYuT7rA== dependencies: tslib "^1.10.0" optionalDependencies: @@ -5669,9 +5669,9 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== simple-git@^2.17.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.26.0.tgz#b760a18d5563390da90c3c06824bc3d0823c4d48" - integrity sha512-I9QIjBNA773X23SZ/S1HFMCA+S//san83Twyd5ffWFjo/sv8VRk7tuck23y1uFWuzTu4KTwDh5LEXyCfEfOWMw== + version "2.27.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.27.0.tgz#3122fb46f6d28853b640fa99f388da2c21f8880d" + integrity sha512-/Q4aolzErYrIx6SgyH421jmtv5l1DaAw+KYWMWy229+isW6yld/nHGxJ2xUR/aeX3SuYJnbucyUigERwaw4Xow== dependencies: "@kwsites/file-exists" "^1.1.1" "@kwsites/promise-deferred" "^1.1.1"