diff --git a/declarations/plugins/IgnorePlugin.d.ts b/declarations/plugins/IgnorePlugin.d.ts index 323535d76..abd77e241 100644 --- a/declarations/plugins/IgnorePlugin.d.ts +++ b/declarations/plugins/IgnorePlugin.d.ts @@ -16,10 +16,6 @@ export type IgnorePluginOptions = resourceRegExp?: RegExp; } | { - /** - * A filter function for context - */ - checkContext?: (context: string) => boolean; /** * A filter function for resource and context */ diff --git a/lib/AsyncDependencyToInitialChunkError.js b/lib/AsyncDependencyToInitialChunkError.js index 0fb1cb96f..570eb51cd 100644 --- a/lib/AsyncDependencyToInitialChunkError.js +++ b/lib/AsyncDependencyToInitialChunkError.js @@ -7,6 +7,7 @@ const WebpackError = require("./WebpackError"); +/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("./Module")} Module */ class AsyncDependencyToInitialChunkError extends WebpackError { @@ -14,7 +15,7 @@ class AsyncDependencyToInitialChunkError extends WebpackError { * Creates an instance of AsyncDependencyToInitialChunkError. * @param {string} chunkName Name of Chunk * @param {Module} module module tied to dependency - * @param {TODO} loc location of dependency + * @param {DependencyLocation} loc location of dependency */ constructor(chunkName, module, loc) { super( diff --git a/lib/Chunk.js b/lib/Chunk.js index 877565b8b..48b580130 100644 --- a/lib/Chunk.js +++ b/lib/Chunk.js @@ -10,6 +10,7 @@ const Entrypoint = require("./Entrypoint"); const { intersect } = require("./util/SetHelpers"); const SortableSet = require("./util/SortableSet"); const { compareModulesByIdOrIdentifier } = require("./util/comparators"); +const { arrayToSetDeprecation } = require("./util/deprecation"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("./ChunkGraph").ChunkFilterPredicate} ChunkFilterPredicate */ @@ -73,9 +74,9 @@ class Chunk { this.filenameTemplate = undefined; /** @private @type {SortableSet} */ this._groups = new SortableSet(undefined, sortChunkGroupById); - // TODO convert files to a Set - /** @type {string[]} */ - this.files = []; + /** @type {Set} */ + this.files = new Set(); + arrayToSetDeprecation(this.files, "Chunk.files"); /** @type {boolean} */ this.rendered = false; /** @type {string=} */ @@ -88,8 +89,6 @@ class Chunk { this.chunkReason = undefined; /** @type {boolean} */ this.extraAsync = false; - // TODO remove this, it's wrong - this.removedModules = undefined; } // TODO remove in webpack 6 diff --git a/lib/ChunkTemplate.js b/lib/ChunkTemplate.js index cf86e2460..bceeed3d9 100644 --- a/lib/ChunkTemplate.js +++ b/lib/ChunkTemplate.js @@ -69,7 +69,6 @@ module.exports = class ChunkTemplate { } /** - * TODO webpack 5: remove moduleTemplate and dependencyTemplates * Updates hash with chunk-specific information from this template * @param {Hash} hash the hash to update * @param {Chunk} chunk the chunk diff --git a/lib/Compilation.js b/lib/Compilation.js index 3a9cd6a76..8f05a0dfe 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -2409,14 +2409,11 @@ class Compilation { asyncLib.forEach( this.chunks, - (chunk, _callback) => { - // TODO Workaround for https://github.com/suguru03/neo-async/issues/63 - const callback = err => process.nextTick(() => _callback(err)); - + (chunk, callback) => { /** @type {RenderManifestEntry[]} */ let manifest; try { - chunk.files = []; + chunk.files.clear(); const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate; @@ -2437,10 +2434,7 @@ class Compilation { } asyncLib.forEach( manifest, - (fileManifest, _callback) => { - // TODO Workaround for https://github.com/suguru03/neo-async/issues/63 - const callback = err => process.nextTick(() => _callback(err)); - + (fileManifest, callback) => { const ident = fileManifest.identifier; const cacheName = `${this.compilerPath}/asset/${ident}`; const usedHash = fileManifest.hash; @@ -2464,7 +2458,7 @@ class Compilation { if (alreadyWritten !== undefined) { if (alreadyWritten.hash !== usedHash) { return callback( - new Error( + new WebpackError( `Conflict: Multiple chunks emit assets to the same filename ${file}` + ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})` ) @@ -2490,7 +2484,7 @@ class Compilation { } if (this.assets[file] && this.assets[file] !== source) { return callback( - new Error( + new WebpackError( `Conflict: Rendering chunk ${chunk.id} ` + `emits to the filename ${file} ` + "which was already written to by something else " + @@ -2499,7 +2493,7 @@ class Compilation { ); } this.assets[file] = source; - chunk.files.push(file); + chunk.files.add(file); this.hooks.chunkAsset.call(chunk, file); alreadyWrittenFiles.set(file, { hash: usedHash, diff --git a/lib/Dependency.js b/lib/Dependency.js index 5b943b497..1dce14315 100644 --- a/lib/Dependency.js +++ b/lib/Dependency.js @@ -50,7 +50,6 @@ const DependencyReference = require("./dependencies/DependencyReference"); class Dependency { constructor() { - // TODO remove in webpack 5 /** @type {boolean} */ this.weak = false; /** @type {boolean} */ diff --git a/lib/ExternalModule.js b/lib/ExternalModule.js index 4bab99125..b1529be33 100644 --- a/lib/ExternalModule.js +++ b/lib/ExternalModule.js @@ -130,8 +130,6 @@ class ExternalModule extends Module { this.externalType = type; /** @type {string} */ this.userRequest = userRequest; - /** @type {boolean} */ - this.external = true; } /** diff --git a/lib/HotModuleReplacementPlugin.js b/lib/HotModuleReplacementPlugin.js index 7aa4b1182..8a348fe89 100644 --- a/lib/HotModuleReplacementPlugin.js +++ b/lib/HotModuleReplacementPlugin.js @@ -357,7 +357,7 @@ class HotModuleReplacementPlugin { const source = entry.render(); compilation.additionalChunkAssets.push(filename); compilation.assets[filename] = source; - currentChunk.files.push(filename); + currentChunk.files.add(filename); compilation.hooks.chunkAsset.call(currentChunk, filename); } hotUpdateMainContent.c.push(chunkId); diff --git a/lib/IgnorePlugin.js b/lib/IgnorePlugin.js index bd75ef64b..a77eb2e2c 100644 --- a/lib/IgnorePlugin.js +++ b/lib/IgnorePlugin.js @@ -37,14 +37,7 @@ class IgnorePlugin { this.options.checkResource && this.options.checkResource(resolveData.request, resolveData.context) ) { - // TODO webpack 5 remove checkContext, as checkResource already gets context - if ("checkContext" in this.options && this.options.checkContext) { - if (this.options.checkContext(resolveData.context)) { - return false; - } - } else { - return false; - } + return false; } if ( diff --git a/lib/SourceMapDevToolPlugin.js b/lib/SourceMapDevToolPlugin.js index f68060eae..af5951ec5 100644 --- a/lib/SourceMapDevToolPlugin.js +++ b/lib/SourceMapDevToolPlugin.js @@ -15,41 +15,76 @@ const { relative, dirname } = require("./util/fs"); const schema = require("../schemas/plugins/SourceMapDevToolPlugin.json"); +/** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../declarations/plugins/SourceMapDevToolPlugin").SourceMapDevToolPluginOptions} SourceMapDevToolPluginOptions */ /** @typedef {import("./Chunk")} Chunk */ +/** @typedef {import("./Compilation")} Compilation */ /** @typedef {import("./Compiler")} Compiler */ +/** @typedef {import("./Module")} Module */ +/** @type {WeakMap}>} */ const assetsCache = new WeakMap(); +/** + * @typedef {Object} SourceMapObject + * @property {string[]} sources + * @property {string[]=} sourcesContent + * @property {string} sourceRoot + * @property {string} file + */ + +/** + * @typedef {Object} SourceMapDevToolTask + * @property {Chunk} chunk + * @property {string} file + * @property {Source} asset + * @property {string} source + * @property {SourceMapObject} sourceMap + * @property {(Module|string)[]} modules + */ + +/** + * @param {string} file the filename + * @param {Chunk} chunk the chunk + * @param {TODO} options options + * @param {Compilation} compilation the compilation + * @returns {SourceMapDevToolTask|undefined} the task for this file + */ const getTaskForFile = (file, chunk, options, compilation) => { const asset = compilation.assets[file]; const cache = assetsCache.get(asset); if (cache && cache.file === file) { for (const cachedFile in cache.assets) { compilation.assets[cachedFile] = cache.assets[cachedFile]; - if (cachedFile !== file) chunk.files.push(cachedFile); + if (cachedFile !== file) chunk.files.add(cachedFile); } return; } - let source, sourceMap; + let source; + /** @type {SourceMapObject} */ + let sourceMap; if (asset.sourceAndMap) { const sourceAndMap = asset.sourceAndMap(options); - sourceMap = sourceAndMap.map; + sourceMap = /** @type {SourceMapObject} */ (sourceAndMap.map); source = sourceAndMap.source; } else { - sourceMap = asset.map(options); + sourceMap = /** @type {SourceMapObject} */ (asset.map(options)); source = asset.source(); } - if (sourceMap) { - return { - chunk, - file, - asset, - source, - sourceMap, - modules: undefined - }; - } + if (!sourceMap || typeof source !== "string") return; + const modules = sourceMap.sources.map(source => { + const module = compilation.findModule(source); + return module || source; + }); + + return { + chunk, + file, + asset, + source, + sourceMap, + modules + }; }; class SourceMapDevToolPlugin { @@ -119,6 +154,7 @@ class SourceMapDevToolPlugin { } reportProgress(0.0); + /** @type {SourceMapDevToolTask[]} */ const tasks = []; files.forEach(({ file, chunk }, idx) => { reportProgress( @@ -129,10 +165,7 @@ class SourceMapDevToolPlugin { const task = getTaskForFile(file, chunk, options, compilation); if (task) { - const modules = task.sourceMap.sources.map(source => { - const module = compilation.findModule(source); - return module || source; - }); + const modules = task.modules; for (let idx = 0; idx < modules.length; idx++) { const module = modules[idx]; @@ -154,8 +187,6 @@ class SourceMapDevToolPlugin { } } - task.modules = modules; - tasks.push(task); } }); @@ -279,7 +310,7 @@ class SourceMapDevToolPlugin { assets[sourceMapFile] = compilation.assets[ sourceMapFile ] = new RawSource(sourceMapString); - chunk.files.push(sourceMapFile); + chunk.files.add(sourceMapFile); } else { if (currentSourceMappingURLComment === false) { throw new Error( diff --git a/lib/SystemMainTemplatePlugin.js b/lib/SystemMainTemplatePlugin.js index b97714f89..413df4d7a 100644 --- a/lib/SystemMainTemplatePlugin.js +++ b/lib/SystemMainTemplatePlugin.js @@ -6,6 +6,7 @@ "use strict"; const { ConcatSource } = require("webpack-sources"); +const ExternalModule = require("./ExternalModule"); const Template = require("./Template"); /** @typedef {import("./Compilation")} Compilation */ @@ -31,7 +32,11 @@ class SystemMainTemplatePlugin { const { mainTemplate, chunkTemplate } = compilation; const onRenderWithEntry = (source, chunk, hash) => { - const externals = chunk.getModules().filter(m => m.external); + const chunkGraph = compilation.chunkGraph; + const modules = chunkGraph + .getChunkModules(chunk) + .filter(m => m instanceof ExternalModule); + const externals = /** @type {ExternalModule[]} */ (modules); // The name this bundle should be registered as with System const name = this.name ? `${JSON.stringify(this.name)}, ` : ""; @@ -39,7 +44,9 @@ class SystemMainTemplatePlugin { // The array of dependencies that are external to webpack and will be provided by System const systemDependencies = JSON.stringify( externals.map(m => - typeof m.request === "object" ? m.request.amd : m.request + typeof m.request === "object" && !Array.isArray(m.request) + ? m.request.amd + : m.request ) ); @@ -48,7 +55,10 @@ class SystemMainTemplatePlugin { // An array of the internal variable names for the webpack externals const externalWebpackNames = externals.map( - m => `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${m.id}`)}__` + m => + `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier( + `${chunkGraph.getModuleId(m)}` + )}__` ); // Declaring variables for the internal variable names for the webpack externals diff --git a/lib/optimize/SplitChunksPlugin.js b/lib/optimize/SplitChunksPlugin.js index 73f5cb956..4fb5cb2b7 100644 --- a/lib/optimize/SplitChunksPlugin.js +++ b/lib/optimize/SplitChunksPlugin.js @@ -214,8 +214,7 @@ const compareEntries = (a, b) => { // 5. by module identifiers modulesA.sort(); modulesB.sort(); - // TODO logic is inverted: fix this - return compareModuleIterables(modulesB, modulesA); + return compareModuleIterables(modulesA, modulesB); }; /** diff --git a/lib/rules/UseEffectRulePlugin.js b/lib/rules/UseEffectRulePlugin.js index f2b61b50e..8d44abb12 100644 --- a/lib/rules/UseEffectRulePlugin.js +++ b/lib/rules/UseEffectRulePlugin.js @@ -157,7 +157,6 @@ class UseEffectRulePlugin { ident: options && typeof options === "object" ? path : undefined } }); - // TODO } } ); diff --git a/lib/stats/DefaultStatsFactoryPlugin.js b/lib/stats/DefaultStatsFactoryPlugin.js index 8e5bbfbbe..795081c23 100644 --- a/lib/stats/DefaultStatsFactoryPlugin.js +++ b/lib/stats/DefaultStatsFactoryPlugin.js @@ -264,7 +264,7 @@ const SIMPLE_EXTRACTORS = { object.name = asset.name; object.size = asset.source.size(); const chunks = Array.from(compilation.chunks).filter(chunk => - chunk.files.includes(asset.name) + chunk.files.has(asset.name) ); object.chunks = Array.from( chunks.reduce((ids, chunk) => { @@ -299,7 +299,7 @@ const SIMPLE_EXTRACTORS = { name, chunks: chunkGroup.chunks.map(c => c.id), assets: chunkGroup.chunks.reduce( - (array, c) => array.concat(c.files || []), + (array, c) => array.concat(Array.from(c.files)), /** @type {string[]} */ ([]) ), children: Object.keys(children).reduce((obj, key) => { @@ -308,7 +308,7 @@ const SIMPLE_EXTRACTORS = { name: group.name, chunks: group.chunks.map(c => c.id), assets: group.chunks.reduce( - (array, c) => array.concat(c.files || []), + (array, c) => array.concat(Array.from(c.files)), /** @type {string[]} */ ([]) ) })); @@ -546,7 +546,7 @@ const SIMPLE_EXTRACTORS = { size: chunkGraph.getChunkModulesSize(chunk), sizes: chunkGraph.getChunkModulesSizes(chunk), names: chunk.name ? [chunk.name] : [], - files: chunk.files.slice(), + files: Array.from(chunk.files), hash: chunk.renderedHash, childrenByOrder: childIdByOrder }); diff --git a/lib/util/deprecation.js b/lib/util/deprecation.js index 53bbd3a32..8894e5862 100644 --- a/lib/util/deprecation.js +++ b/lib/util/deprecation.js @@ -77,6 +77,12 @@ exports.arrayToSetDeprecation = (set, name) => { const dPush = createDeprecation( `${name} was changed from Array to Set (using Array method 'push' is deprecated)` ); + const dLength = createDeprecation( + `${name} was changed from Array to Set (using Array property 'length' is deprecated)` + ); + const dIndexer = createDeprecation( + `${name} was changed from Array to Set (indexing Array is deprecated)` + ); /** * @deprecated * @this {Set} @@ -97,9 +103,30 @@ exports.arrayToSetDeprecation = (set, name) => { ); }; } + const createIndexGetter = index => () => { + dIndexer(); + let i = 0; + for (const item of set) { + if (i++ === index) return item; + } + return undefined; + }; + let indexerDefined = 0; Object.defineProperty(set, "length", { get() { - return set.size; + dLength(); + const length = set.size; + for (indexerDefined; indexerDefined < length; indexerDefined++) { + Object.defineProperty(set, indexerDefined, { + get: createIndexGetter(indexerDefined), + set(value) { + throw new Error( + `${name} was changed from Array to Set (indexing Array with write is not possible)` + ); + } + }); + } + return length; }, set(value) { throw new Error( @@ -107,4 +134,5 @@ exports.arrayToSetDeprecation = (set, name) => { ); } }); + set[Symbol.isConcatSpreadable] = true; }; diff --git a/lib/wasm/WebAssemblyGenerator.js b/lib/wasm/WebAssemblyGenerator.js index d5aa3024d..3a064012b 100644 --- a/lib/wasm/WebAssemblyGenerator.js +++ b/lib/wasm/WebAssemblyGenerator.js @@ -45,8 +45,6 @@ const compose = (...fns) => { ); }; -// TODO replace with @callback - /** * Removes the start instruction * diff --git a/package.json b/package.json index f5a4d7fef..b6a89a206 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "loader-runner": "3.0.0", "loader-utils": "^1.1.0", "memory-fs": "~0.4.1", - "neo-async": "^2.5.0", + "neo-async": "^2.6.0", "schema-utils": "^1.0.0", "tapable": "2.0.0-beta.4", "terser-webpack-plugin": "^1.2.1", diff --git a/schemas/plugins/IgnorePlugin.json b/schemas/plugins/IgnorePlugin.json index c873f07f7..bdbf511b6 100644 --- a/schemas/plugins/IgnorePlugin.json +++ b/schemas/plugins/IgnorePlugin.json @@ -21,11 +21,6 @@ "type": "object", "additionalProperties": false, "properties": { - "checkContext": { - "description": "A filter function for context", - "instanceof": "Function", - "tsType": "((context: string) => boolean)" - }, "checkResource": { "description": "A filter function for resource and context", "instanceof": "Function", diff --git a/test/Examples.test.js b/test/Examples.test.js index fc22ee891..8fec10abc 100644 --- a/test/Examples.test.js +++ b/test/Examples.test.js @@ -38,13 +38,6 @@ describe("Examples", () => { options.output.publicPath = "dist/"; if (!options.entry) options.entry = "./example.js"; if (!options.plugins) options.plugins = []; - // To support deprecated loaders - // TODO remove in webpack 5 - options.plugins.push( - new webpack.LoaderOptionsPlugin({ - options: {} - }) - ); } webpack(options, (err, stats) => { if (err) return done(err); diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap index 70197db79..2136e33fc 100644 --- a/test/__snapshots__/StatsTestCases.test.js.snap +++ b/test/__snapshots__/StatsTestCases.test.js.snap @@ -2731,9 +2731,9 @@ Child name-too-long: Child custom-chunks-filter: Entrypoint main = default/main.js Entrypoint a = default/a.js - Entrypoint b = default/282.js default/954.js default/767.js default/b.js - Entrypoint c = default/282.js default/769.js default/767.js default/c.js - chunk {128} default/b.js (b) 92 bytes (javascript) 2.57 KiB (runtime) ={282}= ={767}= ={954}= [entry] [rendered] + Entrypoint b = default/282.js default/954.js default/568.js default/b.js + Entrypoint c = default/282.js default/769.js default/568.js default/c.js + chunk {128} default/b.js (b) 92 bytes (javascript) 2.57 KiB (runtime) ={282}= ={568}= ={954}= [entry] [rendered] > ./b b [996] ./b.js 72 bytes {128} {334} [built] + 2 hidden root modules @@ -2758,23 +2758,23 @@ Child custom-chunks-filter: chunk {383} default/async-c.js (async-c) 72 bytes <{179}> ={282}= ={568}= ={767}= ={769}= [rendered] > ./c [10] ./index.js 3:0-47 [460] ./c.js 72 bytes {383} {459} [built] - chunk {459} default/c.js (c) 92 bytes (javascript) 2.57 KiB (runtime) ={282}= ={767}= ={769}= [entry] [rendered] + chunk {459} default/c.js (c) 92 bytes (javascript) 2.57 KiB (runtime) ={282}= ={568}= ={769}= [entry] [rendered] > ./c c [460] ./c.js 72 bytes {383} {459} [built] + 2 hidden root modules + 1 hidden dependent module - chunk {568} default/568.js 20 bytes <{179}> <{282}> <{767}> <{786}> <{794}> <{954}> ={137}= ={282}= ={334}= ={383}= ={767}= ={769}= ={954}= [rendered] split chunk (cache group: default) + chunk {568} default/568.js 20 bytes <{179}> <{282}> <{767}> <{786}> <{794}> <{954}> ={128}= ={137}= ={282}= ={334}= ={383}= ={459}= ={767}= ={769}= ={954}= [initial] [rendered] split chunk (cache group: default) > ./b [10] ./index.js 2:0-47 > ./c [10] ./index.js 3:0-47 > ./g ./a.js 6:0-47 - [568] ./f.js 20 bytes {128} {459} {568} [built] - chunk {767} default/767.js 20 bytes <{179}> ={128}= ={282}= ={334}= ={383}= ={459}= ={568}= ={769}= ={794}= ={954}= >{137}< >{568}< [initial] [rendered] split chunk (cache group: default) + > ./b b + > ./c c + [568] ./f.js 20 bytes {568} [built] + chunk {767} default/767.js 20 bytes <{179}> ={282}= ={334}= ={383}= ={568}= ={769}= ={794}= ={954}= >{137}< >{568}< [rendered] split chunk (cache group: default) > ./a [10] ./index.js 1:0-47 > ./b [10] ./index.js 2:0-47 > ./c [10] ./index.js 3:0-47 - > ./b b - > ./c c - [767] ./d.js 20 bytes {767} {786} [built] + [767] ./d.js 20 bytes {128} {459} {767} {786} [built] chunk {769} default/769.js 20 bytes <{179}> ={282}= ={383}= ={459}= ={568}= ={767}= [initial] [rendered] split chunk (cache group: defaultVendors) > ./c [10] ./index.js 3:0-47 > ./c c diff --git a/test/configCases/ignore/checkContext/webpack.config.js b/test/configCases/ignore/checkContext/webpack.config.js index 77dce7ce9..65ca0c931 100644 --- a/test/configCases/ignore/checkContext/webpack.config.js +++ b/test/configCases/ignore/checkContext/webpack.config.js @@ -6,11 +6,8 @@ module.exports = { entry: "./test.js", plugins: [ new IgnorePlugin({ - checkResource(resource) { - return /ignored-module/.test(resource); - }, - checkContext(context) { - return /folder-b/.test(context); + checkResource(resource, context) { + return /ignored-module/.test(resource) && /folder-b/.test(context); } }) ]