diff --git a/lib/ChunkGroup.js b/lib/ChunkGroup.js index 2fcb71d1d9b..ab0fbde0351 100644 --- a/lib/ChunkGroup.js +++ b/lib/ChunkGroup.js @@ -115,16 +115,23 @@ class ChunkGroup { */ addOptions(options) { for (const _key of Object.keys(options)) { - const key = /** @type {keyof ChunkGroupOptions} */ (_key); + const key = + /** @type {keyof ChunkGroupOptions} */ + (_key); if (this.options[key] === undefined) { - /** @type {TODO} */ + /** @type {EXPECTED_ANY} */ (this.options)[key] = options[key]; } else if (this.options[key] !== options[key]) { if (key.endsWith("Order")) { - /** @type {TODO} */ - (this.options)[key] = Math.max( - /** @type {number} */ (this.options[key]), - /** @type {number} */ (options[key]) + const orderKey = + /** @type {Exclude} */ + (key); + + this.options[orderKey] = Math.max( + /** @type {number} */ + (this.options[orderKey]), + /** @type {number} */ + (options[orderKey]) ); } else { throw new Error( diff --git a/lib/Compilation.js b/lib/Compilation.js index 24c52d09d69..dfc3c71c972 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -2354,6 +2354,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si */ _addEntryItem(context, entry, target, options, callback) { const { name } = options; + /** @type {EntryData | undefined} */ let entryData = name !== undefined ? this.entries.get(name) : this.globalEntry; if (entryData === undefined) { diff --git a/lib/ConstPlugin.js b/lib/ConstPlugin.js index 11020251bf3..2950799b6b1 100644 --- a/lib/ConstPlugin.js +++ b/lib/ConstPlugin.js @@ -179,53 +179,21 @@ class ConstPlugin { ? statement.alternate : statement.consequent; if (branchToRemove) { - // Before removing the dead branch, the hoisted declarations - // must be collected. - // - // Given the following code: - // - // if (true) f() else g() - // if (false) { - // function f() {} - // const g = function g() {} - // if (someTest) { - // let a = 1 - // var x, {y, z} = obj - // } - // } else { - // … - // } - // - // the generated code is: - // - // if (true) f() else {} - // if (false) { - // var f, x, y, z; (in loose mode) - // var x, y, z; (in strict mode) - // } else { - // … - // } - // - // NOTE: When code runs in strict mode, `var` declarations - // are hoisted but `function` declarations don't. - // - const declarations = parser.scope.isStrict - ? getHoistedDeclarations(branchToRemove, false) - : getHoistedDeclarations(branchToRemove, true); - const replacement = - declarations.length > 0 - ? `{ var ${declarations.join(", ")}; }` - : "{}"; - const dep = new ConstDependency( - replacement, - /** @type {Range} */ (branchToRemove.range) - ); - dep.loc = /** @type {SourceLocation} */ (branchToRemove.loc); - parser.state.module.addPresentationalDependency(dep); + this.eliminateUnusedStatement(parser, branchToRemove); } return bool; } }); + parser.hooks.unusedStatement.tap(PLUGIN_NAME, statement => { + if ( + parser.scope.isAsmJs || + // Check top level scope here again + parser.scope.topLevelScope === true + ) + return; + this.eliminateUnusedStatement(parser, statement); + return true; + }); parser.hooks.expressionConditionalOperator.tap( PLUGIN_NAME, expression => { @@ -534,6 +502,56 @@ class ConstPlugin { } ); } + + /** + * Eliminate an unused statement. + * @param {JavascriptParser} parser the parser + * @param {Statement} statement the statement to remove + * @returns {void} + */ + eliminateUnusedStatement(parser, statement) { + // Before removing the unused branch, the hoisted declarations + // must be collected. + // + // Given the following code: + // + // if (true) f() else g() + // if (false) { + // function f() {} + // const g = function g() {} + // if (someTest) { + // let a = 1 + // var x, {y, z} = obj + // } + // } else { + // … + // } + // + // the generated code is: + // + // if (true) f() else {} + // if (false) { + // var f, x, y, z; (in loose mode) + // var x, y, z; (in strict mode) + // } else { + // … + // } + // + // NOTE: When code runs in strict mode, `var` declarations + // are hoisted but `function` declarations don't. + // + const declarations = parser.scope.isStrict + ? getHoistedDeclarations(statement, false) + : getHoistedDeclarations(statement, true); + const replacement = + declarations.length > 0 ? `{ var ${declarations.join(", ")}; }` : "{}"; + const dep = new ConstDependency( + `// removed by dead control flow\n${replacement}`, + /** @type {Range} */ (statement.range) + ); + dep.loc = /** @type {SourceLocation} */ (statement.loc); + parser.state.module.addPresentationalDependency(dep); + } } module.exports = ConstPlugin; diff --git a/lib/asset/AssetGenerator.js b/lib/asset/AssetGenerator.js index 58e82589e31..b145641d70b 100644 --- a/lib/asset/AssetGenerator.js +++ b/lib/asset/AssetGenerator.js @@ -657,6 +657,7 @@ class AssetGenerator extends Generator { * @returns {SourceTypes} available types (do not mutate) */ getTypes(module) { + /** @type {Set} */ const sourceTypes = new Set(); const connections = this._moduleGraph.getIncomingConnections(module); @@ -669,27 +670,25 @@ class AssetGenerator extends Generator { } if ((module.buildInfo && module.buildInfo.dataUrl) || this.emit === false) { - if (sourceTypes) { + if (sourceTypes.size > 0) { if (sourceTypes.has("javascript") && sourceTypes.has("css")) { return JS_AND_CSS_URL_TYPES; - } else if (sourceTypes.has("javascript")) { - return JS_TYPES; } else if (sourceTypes.has("css")) { return CSS_URL_TYPES; } + return JS_TYPES; } return NO_TYPES; } - if (sourceTypes) { + if (sourceTypes.size > 0) { if (sourceTypes.has("javascript") && sourceTypes.has("css")) { return ASSET_AND_JS_AND_CSS_URL_TYPES; - } else if (sourceTypes.has("javascript")) { - return ASSET_AND_JS_TYPES; } else if (sourceTypes.has("css")) { return ASSET_AND_CSS_URL_TYPES; } + return ASSET_AND_JS_TYPES; } return ASSET_TYPES; diff --git a/lib/asset/AssetSourceGenerator.js b/lib/asset/AssetSourceGenerator.js index 2135e9e694c..512fb440966 100644 --- a/lib/asset/AssetSourceGenerator.js +++ b/lib/asset/AssetSourceGenerator.js @@ -122,6 +122,7 @@ class AssetSourceGenerator extends Generator { * @returns {SourceTypes} available types (do not mutate) */ getTypes(module) { + /** @type {Set} */ const sourceTypes = new Set(); const connections = this._moduleGraph.getIncomingConnections(module); @@ -133,12 +134,13 @@ class AssetSourceGenerator extends Generator { sourceTypes.add(connection.originModule.type.split("/")[0]); } - if (sourceTypes.has("javascript") && sourceTypes.has("css")) { - return JS_AND_CSS_URL_TYPES; - } else if (sourceTypes.has("javascript")) { + if (sourceTypes.size > 0) { + if (sourceTypes.has("javascript") && sourceTypes.has("css")) { + return JS_AND_CSS_URL_TYPES; + } else if (sourceTypes.has("css")) { + return CSS_URL_TYPES; + } return JS_TYPES; - } else if (sourceTypes.has("css")) { - return CSS_URL_TYPES; } return NO_TYPES; diff --git a/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js b/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js index 7455031117c..9f9bc0c0ac1 100644 --- a/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +++ b/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js @@ -109,7 +109,10 @@ module.exports = class RequireEnsureDependenciesBlockParserPlugin { } if (successExpression) { if (successExpression.fn.body.type === "BlockStatement") { + // Opt-out of Dead Control Flow detection for this block + const oldTerminated = parser.scope.terminated; parser.walkStatement(successExpression.fn.body); + parser.scope.terminated = oldTerminated; } else { parser.walkExpression(successExpression.fn.body); } diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index 1e2b3253883..48272aad6dd 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -578,7 +578,9 @@ class JavascriptParser extends Parser { /** @type {SyncBailHook<[ThrowStatement | ReturnStatement], boolean | void>} */ terminate: new SyncBailHook(["statement"]), /** @type {SyncBailHook<[Program, Comment[]], boolean | void>} */ - finish: new SyncBailHook(["ast", "comments"]) + finish: new SyncBailHook(["ast", "comments"]), + /** @type {SyncBailHook<[Statement], boolean | void>} */ + unusedStatement: new SyncBailHook(["statement"]) }); this.sourceType = sourceType; /** @type {ScopeInfo} */ @@ -1939,8 +1941,13 @@ class JavascriptParser extends Parser { for (let index = 0, len = statements.length; index < len; index++) { const statement = statements[index]; - if (onlyFunctionDeclaration && statement.type !== "FunctionDeclaration") + if ( + onlyFunctionDeclaration && + statement.type !== "FunctionDeclaration" && + this.hooks.unusedStatement.call(/** @type {Statement} */ (statement)) + ) { continue; + } this.walkStatement(statement); diff --git a/lib/stats/DefaultStatsPrinterPlugin.js b/lib/stats/DefaultStatsPrinterPlugin.js index 4936714333e..30f260da974 100644 --- a/lib/stats/DefaultStatsPrinterPlugin.js +++ b/lib/stats/DefaultStatsPrinterPlugin.js @@ -1454,7 +1454,12 @@ const AVAILABLE_COLORS = { magenta: "\u001B[1m\u001B[35m" }; -/** @typedef {Required<{ [Key in keyof KnownStatsPrinterFormatters]: (value: Parameters>[0], options: Required & StatsPrinterContext, ...args: TODO[]) => string }>} AvailableFormats */ +/** + * @template T + * @typedef {T extends [infer Head, ...infer Tail] ? Tail : undefined} Tail + */ + +/** @typedef {Required<{ [Key in keyof KnownStatsPrinterFormatters]: (value: Parameters>[0], options: Required & StatsPrinterContextWithExtra, ...args: Tail>>) => string }>} AvailableFormats */ /** @type {AvailableFormats} */ const AVAILABLE_FORMATS = { @@ -1629,21 +1634,16 @@ class DefaultStatsPrinterPlugin { context[color] = str => str; } } - for (const format of Object.keys(AVAILABLE_FORMATS)) { + for (const _format of Object.keys(AVAILABLE_FORMATS)) { + const format = + /** @type {keyof KnownStatsPrinterFormatters} */ + (_format); + context[format] = - /** - * @param {string | number} content content - * @param {...TODO} args args - * @returns {string} result - */ + /** @type {(content: Parameters>[0], ...args: Tail>>) => string} */ (content, ...args) => /** @type {TODO} */ - ( - AVAILABLE_FORMATS[ - /** @type {keyof AvailableFormats} */ - (format) - ] - )( + (AVAILABLE_FORMATS)[format]( content, /** @type {StatsPrinterContext & Required} */ (context), diff --git a/package.json b/package.json index 380457d8dde..73569443e66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webpack", - "version": "5.99.8", + "version": "5.99.9", "author": "Tobias Koppers @sokra", "description": "Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.", "license": "MIT", diff --git a/test/BenchmarkTestCases.benchmark.mjs b/test/BenchmarkTestCases.benchmark.mjs index df61971d4e9..10cd1313172 100644 --- a/test/BenchmarkTestCases.benchmark.mjs +++ b/test/BenchmarkTestCases.benchmark.mjs @@ -321,9 +321,10 @@ const baseOutputPath = path.join(__dirname, "js", "benchmark"); const bench = withCodSpeed( new Bench({ - warmup: true, now: hrtimeNow, - throws: true + throws: true, + warmup: true, + time: 30000 }) ); diff --git a/test/configCases/parsing/dead-code-elimination-require-ensure/foo.js b/test/configCases/parsing/dead-code-elimination-require-ensure/foo.js new file mode 100644 index 00000000000..442b898d8a6 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination-require-ensure/foo.js @@ -0,0 +1 @@ +export default "foo" \ No newline at end of file diff --git a/test/configCases/parsing/dead-code-elimination-require-ensure/index.js b/test/configCases/parsing/dead-code-elimination-require-ensure/index.js new file mode 100644 index 00000000000..dcf22e04465 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination-require-ensure/index.js @@ -0,0 +1,14 @@ +it("should compile and work", done => { + require.ensure( + ["./foo"], + () => { + throw new Error("error"); + }, + () => { + import("./foo").then(m => { + expect(m.default).toBe("foo"); + done(); + }); + } + ); +}); diff --git a/test/configCases/parsing/dead-code-elimination-require-ensure/webpack.config.js b/test/configCases/parsing/dead-code-elimination-require-ensure/webpack.config.js new file mode 100644 index 00000000000..e30e85e9364 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination-require-ensure/webpack.config.js @@ -0,0 +1,6 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + optimization: { + minimize: false + } +}; diff --git a/test/configCases/parsing/dead-code-elimination/esm1.js b/test/configCases/parsing/dead-code-elimination/esm1.js new file mode 100644 index 00000000000..a87670f790b --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/esm1.js @@ -0,0 +1,18 @@ +it("Should eliminate hoisted function in ESM because of default strict mode", () => { + expect(() => { + fnDecl; + }).toThrow(); + try { + throw new Error(); + } catch (e) { + return; + } + { + function fnDecl() { + expect(true).toBe(true); + } + } + expect(true).toBe(false); +}); + +export default "esm1"; diff --git a/test/configCases/parsing/dead-code-elimination/esm2.js b/test/configCases/parsing/dead-code-elimination/esm2.js new file mode 100644 index 00000000000..dbc337f05b8 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/esm2.js @@ -0,0 +1 @@ +export default "esm2"; diff --git a/test/configCases/parsing/dead-code-elimination/index.js b/test/configCases/parsing/dead-code-elimination/index.js new file mode 100644 index 00000000000..e08ff1ae5bd --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/index.js @@ -0,0 +1,118 @@ +const esm1 = require("./esm1"); + +expect(esm1.default).toBe("esm1"); + +it("Shouldn't eliminate hoisted function in current block scope", () => { + expect(() => { + funcDecl; + }).not.toThrow(); + + funcDecl(); + if (true) return; + function funcDecl() {} + import("./esm3").then(() => { + expect(true).toBe(false); + }); +}); + +it("Shouldn't eliminate hoisted variable", () => { + expect(() => { + varDecl; + }).not.toThrow(); + try { + throw new Error(); + } catch (e) { + return; + } + var varDecl = "foo"; + expect(true).toBe(false); + import("./esm3").then(() => { + expect(true).toBe(false); + }); +}); + +it("Shouldn't eliminate hoisted variable (more complex). From test/configCases/parsing/issue-4857/index.js", () => { + expect(() => { + a; + b; + c; + d; + e; + f; + g; + h; + i; + j; + k; + l; + m; + n; + o; + }).not.toThrow(); + expect(() => { + withVar; + }).toThrow(); + try { + } finally { + return; + } + expect(true).toBe(false); + var a, + [, , b] = [], + { c, D: d, ["E"]: e = 2 } = {}; + var [{ ["_"]: f }, ...g] = []; + do { + switch (g) { + default: + var h; + break; + } + loop: for (var i; ; ) for (var j in {}) for (var k of {}) break; + try { + var l; + } catch (e) { + var m; + } finally { + var n; + } + { + var o; + } + } while (true); + with (o) { + var withVar; + } + import("./esm3").then(() => { + expect(true).toBe(false); + }); +}); + +it("Should eliminate hoisted function in strict mode", () => { + "use strict"; + expect(() => { + funcDecl; + }).toThrow(); + + if (true) return; + + { + function funcDecl() {} + expect(true).toBe(false); + } +}); + +it("Shouldn't eliminate hoisted function in sloppy mode", () => { + expect(() => { + funcDecl; + }).not.toThrow(); + if (true) return; + { + function funcDecl() {} + expect(true).toBe(false); + } +}); +if (true) { + return; +} +// We won't terminate in top level scope although it won't run. +require("./esm2"); diff --git a/test/configCases/parsing/dead-code-elimination/test.config.js b/test/configCases/parsing/dead-code-elimination/test.config.js new file mode 100644 index 00000000000..ec5cd05981f --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/test.config.js @@ -0,0 +1,5 @@ +module.exports = { + findBundle() { + return ["test.js", `bundle0.js`]; + } +}; diff --git a/test/configCases/parsing/dead-code-elimination/test.js b/test/configCases/parsing/dead-code-elimination/test.js new file mode 100644 index 00000000000..0e368560062 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/test.js @@ -0,0 +1,6 @@ +it("should work", () => { + const stats = __STATS__.children[__STATS_I__]; + expect( + stats.modules.filter(m => m.name.startsWith("./esm")).length === 2 + ).toBe(true); +}); diff --git a/test/configCases/parsing/dead-code-elimination/webpack.config.js b/test/configCases/parsing/dead-code-elimination/webpack.config.js new file mode 100644 index 00000000000..34bd27d45b3 --- /dev/null +++ b/test/configCases/parsing/dead-code-elimination/webpack.config.js @@ -0,0 +1,49 @@ +const path = require("path"); +const fs = require("fs"); +const webpack = require("../../../../"); + +/** @type {import("../../../../").Configuration[]} */ +module.exports = [ + { + optimization: { + minimize: false + }, + module: { + rules: [ + { + test: /index.js$/, + type: "javascript/dynamic" + }, + { + test: /esm/, + type: "javascript/esm" + } + ] + }, + plugins: [ + { + apply(compiler) { + compiler.hooks.compilation.tap("Test", compilation => { + compilation.hooks.processAssets.tap( + { + name: "copy-webpack-plugin", + stage: + compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL + }, + () => { + const data = fs.readFileSync( + path.resolve(__dirname, "./test.js") + ); + + compilation.emitAsset( + "test.js", + new webpack.sources.RawSource(data) + ); + } + ); + }); + } + } + ] + } +]; diff --git a/test/configCases/parsing/issue-19514/index.js b/test/configCases/parsing/issue-19514/index.js new file mode 100644 index 00000000000..3e35f41b84d --- /dev/null +++ b/test/configCases/parsing/issue-19514/index.js @@ -0,0 +1,12 @@ +it("should compile and work", done => { + function main() { + if (!import.meta.webpackHot) { + return; + } + if (import.meta.webpackHot.status() !== "idle") { + console.log("idle"); + } + } + main(); + done(); +}); diff --git a/test/configCases/parsing/issue-19514/webpack.config.js b/test/configCases/parsing/issue-19514/webpack.config.js new file mode 100644 index 00000000000..f2b98bcca3f --- /dev/null +++ b/test/configCases/parsing/issue-19514/webpack.config.js @@ -0,0 +1,12 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "node", + output: { + library: { + type: "commonjs" + } + }, + optimization: { + minimize: false + } +}; diff --git a/test/configCases/parsing/issue-4857/index.js b/test/configCases/parsing/issue-4857/index.js index a1d24f3ae88..0d917281c99 100644 --- a/test/configCases/parsing/issue-4857/index.js +++ b/test/configCases/parsing/issue-4857/index.js @@ -2,21 +2,21 @@ it("should transpile unreachable branches", () => { let count = 0; // BlockStatement - if(true) { + if (true) { count++; } else { import("NOT_REACHABLE"); } - if(false) { + if (false) { import("NOT_REACHABLE"); } else { count++; } // ExpressionStatement - if(true) count++; + if (true) count++; else import("NOT_REACHABLE"); - if(false) import("NOT_REACHABLE"); + if (false) import("NOT_REACHABLE"); else count++; // ConditionalExpression @@ -27,22 +27,21 @@ it("should transpile unreachable branches", () => { }); it("should not remove hoisted variable declarations", () => { - if(false) { - var a, [,,b,] = [], {c, D: d, ["E"]: e = 2} = {}; - var [{["_"]: f}, ...g] = []; + if (false) { + var a, + [, , b] = [], + { c, D: d, ["E"]: e = 2 } = {}; + var [{ ["_"]: f }, ...g] = []; do { - switch(g) { + switch (g) { default: var h; break; } - loop: for(var i;;) - for(var j in {}) - for(var k of {}) - break; + loop: for (var i; ; ) for (var j in {}) for (var k of {}) break; try { var l; - } catch(e) { + } catch (e) { var m; } finally { var n; @@ -50,7 +49,7 @@ it("should not remove hoisted variable declarations", () => { { var o; } - } while(true); + } while (true); with (o) { var withVar; } @@ -71,27 +70,27 @@ it("should not remove hoisted variable declarations", () => { m; n; o; - }).not.toThrowError(); + }).not.toThrow(); expect(() => { withVar; - }).toThrowError(); + }).toThrow(); }); it("should not remove hoisted function declarations in loose mode", () => { - if(false) { + if (false) { function funcDecl() {} } expect(() => { funcDecl; - }).not.toThrowError(); + }).not.toThrow(); }); it("should remove hoisted function declarations in strict mode", () => { "use strict"; - if(false) { + if (false) { function funcDecl() {} } expect(() => { funcDecl; - }).toThrowError(); + }).toThrow(); }); diff --git a/test/hotCases/asset-modules-source/lazy-compilation/file.text b/test/hotCases/asset-modules-source/lazy-compilation/file.text new file mode 100644 index 00000000000..bab8b92bab1 --- /dev/null +++ b/test/hotCases/asset-modules-source/lazy-compilation/file.text @@ -0,0 +1,6 @@ +We use `NEXT()` to trigger one compilation to simulate lazy compilation behavior. +So this initial content will be ignored. +--- +A +--- +B diff --git a/test/hotCases/asset-modules-source/lazy-compilation/index.js b/test/hotCases/asset-modules-source/lazy-compilation/index.js new file mode 100644 index 00000000000..ba84c405b6d --- /dev/null +++ b/test/hotCases/asset-modules-source/lazy-compilation/index.js @@ -0,0 +1,21 @@ +const getFile = name => + __non_webpack_require__("fs").readFileSync( + __non_webpack_require__("path").join(__dirname, name), + "utf-8" + ); + +it("should work", async function (done) { + let promise = import("./file.text"); + NEXT( + require("../../update")(done, true, () => { + promise.then(() => { + expect(getFile("./assets/file.text")).toContain("A"); + module.hot.accept("./file.text", () => { + expect(getFile("./assets/file.text")).toContain("B"); + done(); + }); + NEXT(require("../../update")(done)); + }); + }) + ); +}); diff --git a/test/hotCases/asset-modules-source/lazy-compilation/webpack.config.js b/test/hotCases/asset-modules-source/lazy-compilation/webpack.config.js new file mode 100644 index 00000000000..231f79a619f --- /dev/null +++ b/test/hotCases/asset-modules-source/lazy-compilation/webpack.config.js @@ -0,0 +1,25 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + experiments: { + lazyCompilation: { + entries: false, + imports: true + } + }, + node: { + __dirname: false + }, + module: { + generator: { + asset: { + filename: "assets/[name][ext]" + } + }, + rules: [ + { + test: /file\.text$/, + type: "asset/resource" + } + ] + } +}; diff --git a/types.d.ts b/types.d.ts index 9dd9839ffb6..1d6f52bdbe9 100644 --- a/types.d.ts +++ b/types.d.ts @@ -6501,6 +6501,7 @@ declare class JavascriptParser extends Parser { program: SyncBailHook<[Program, Comment[]], boolean | void>; terminate: SyncBailHook<[ReturnStatement | ThrowStatement], boolean | void>; finish: SyncBailHook<[Program, Comment[]], boolean | void>; + unusedStatement: SyncBailHook<[Statement], boolean | void>; }>; sourceType: "module" | "auto" | "script"; scope: ScopeInfo; diff --git a/yarn.lock b/yarn.lock index 5de47c7aa6b..a411c2b48ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6212,9 +6212,9 @@ synckit@^0.9.1: tslib "^2.6.2" tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + version "2.2.2" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" + integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== tar@^7.4.3: version "7.4.3" @@ -6556,9 +6556,9 @@ wast-loader@^1.12.1: wabt "1.0.0-nightly.20180421" watchpack@^2.4.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" - integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + version "2.4.3" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.3.tgz#110b3a600c525f6a39ab66cf354cf08b205c29dc" + integrity sha512-adBYQLivcg1jbdKEJeqScJJFvgm4qY9+3tXw+jdG6lkVeqRJEtiQmSWjmth8GKmDZuX7sYM4YFxQsf0AzMfGGw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2"