diff --git a/CHANGELOG.md b/CHANGELOG.md index ee8f1299..4b467235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ If you manually specified the option `insert`, now it can only be a selector or the path to the module. Follow the [style-loader migration guide](https://github.com/webpack-contrib/style-loader/releases/tag/v4.0.0) to upgrade! +* Re-add `webpack-manifest-plugin` dependency, which was previously embedded in https://github.com/symfony/webpack-encore/pull/921 + ## 5.1.0 ### Features diff --git a/eslint.config.js b/eslint.config.js index 7a0f66c2..1b572f28 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,7 +30,6 @@ module.exports = [ ...globals.node, }, }, - ignores: ['lib/webpack-manifest-plugin'] }, { 'rules': { diff --git a/lib/plugins/manifest.js b/lib/plugins/manifest.js index 6b3adaa1..d504350a 100644 --- a/lib/plugins/manifest.js +++ b/lib/plugins/manifest.js @@ -13,7 +13,7 @@ * @import WebpackConfig from '../WebpackConfig' */ -const { WebpackManifestPlugin } = require('../webpack-manifest-plugin'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); const PluginPriorities = require('./plugin-priorities'); const applyOptionsCallback = require('../utils/apply-options-callback'); const copyEntryTmpName = require('../utils/copyEntryTmpName'); diff --git a/lib/webpack-manifest-plugin/LICENSE b/lib/webpack-manifest-plugin/LICENSE deleted file mode 100644 index e540a67b..00000000 --- a/lib/webpack-manifest-plugin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Dane Thurber - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/lib/webpack-manifest-plugin/README.md b/lib/webpack-manifest-plugin/README.md deleted file mode 100644 index ea088324..00000000 --- a/lib/webpack-manifest-plugin/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# webpack-manifest-plugin - -This is a copy of https://github.com/shellscape/webpack-manifest-plugin -at sha: 9f408f609d9b1af255491036b6fc127777ee6f9a. - -It has been modified to fix this bug: https://github.com/shellscape/webpack-manifest-plugin/pull/249 diff --git a/lib/webpack-manifest-plugin/helpers.js b/lib/webpack-manifest-plugin/helpers.js deleted file mode 100644 index 5dfd0b4f..00000000 --- a/lib/webpack-manifest-plugin/helpers.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of the Symfony Webpack Encore package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -const { dirname, join, basename } = require('path'); - -const generateManifest = (compilation, files, { generate, seed = {} }) => { - let result; - if (generate) { - const entrypointsArray = Array.from(compilation.entrypoints.entries()); - const entrypoints = entrypointsArray.reduce( - (e, [name, entrypoint]) => Object.assign(e, { [name]: entrypoint.getFiles() }), - {} - ); - result = generate(seed, files, entrypoints); - } else { - result = files.reduce( - (manifest, file) => Object.assign(manifest, { [file.name]: file.path }), - seed - ); - } - - return result; -}; - -const getFileType = (fileName, { transformExtensions }) => { - const replaced = fileName.replace(/\?.*/, ''); - const split = replaced.split('.'); - const extension = split.pop(); - return transformExtensions.test(extension) ? `${split.pop()}.${extension}` : extension; -}; - -const reduceAssets = (files, asset, moduleAssets) => { - let name; - if (moduleAssets[asset.name]) { - name = moduleAssets[asset.name]; - } else if (asset.info.sourceFilename) { - name = join(dirname(asset.name), basename(asset.info.sourceFilename)); - } - - if (name) { - return files.concat({ - path: asset.name, - name, - isInitial: false, - isChunk: false, - isAsset: true, - isModuleAsset: true - }); - } - - const isEntryAsset = asset.chunks && asset.chunks.length > 0; - if (isEntryAsset) { - return files; - } - - return files.concat({ - path: asset.name, - name: asset.name, - isInitial: false, - isChunk: false, - isAsset: true, - isModuleAsset: false - }); -}; - -const reduceChunk = (files, chunk, options, auxiliaryFiles) => { - // auxiliary files contain things like images, fonts AND, most - // importantly, other files like .map sourcemap files - // we modify the auxiliaryFiles so that we can add any of these - // to the manifest that was not added by another method - // (sourcemaps files are not added via any other method) - Array.from(chunk.auxiliaryFiles || []).forEach((auxiliaryFile) => { - auxiliaryFiles[auxiliaryFile] = { - path: auxiliaryFile, - name: basename(auxiliaryFile), - isInitial: false, - isChunk: false, - isAsset: true, - isModuleAsset: true - }; - }); - - return Array.from(chunk.files).reduce((prev, path) => { - let name = chunk.name ? chunk.name : null; - // chunk name, or for nameless chunks, just map the files directly. - name = name - ? options.useEntryKeys && !path.endsWith('.map') - ? name - : `${name}.${getFileType(path, options)}` - : path; - - return prev.concat({ - path, - chunk, - name, - isInitial: chunk.isOnlyInitial(), - isChunk: true, - isAsset: false, - isModuleAsset: false - }); - }, files); -}; - -const standardizeFilePaths = (file) => { - const result = Object.assign({}, file); - result.name = file.name.replace(/\\/g, '/'); - result.path = file.path.replace(/\\/g, '/'); - return result; -}; - -const transformFiles = (files, options) => - ['filter', 'map', 'sort'] - .filter((fname) => !!options[fname]) - // TODO: deprecate these - .reduce((prev, fname) => prev[fname](options[fname]), files) - .map(standardizeFilePaths); - -module.exports = { generateManifest, reduceAssets, reduceChunk, transformFiles }; diff --git a/lib/webpack-manifest-plugin/hooks.js b/lib/webpack-manifest-plugin/hooks.js deleted file mode 100644 index a77eb6bc..00000000 --- a/lib/webpack-manifest-plugin/hooks.js +++ /dev/null @@ -1,154 +0,0 @@ -/* - * This file is part of the Symfony Webpack Encore package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -const { mkdirSync, writeFileSync } = require('fs'); -const { basename, dirname, join } = require('path'); - -const { SyncWaterfallHook } = require('tapable'); -const webpack = require('webpack'); - -// eslint-disable-next-line n/no-extraneous-require -const { RawSource } = webpack.sources || require('webpack-sources'); - -const { generateManifest, reduceAssets, reduceChunk, transformFiles } = require('./helpers'); - -const compilerHookMap = new WeakMap(); - -const getCompilerHooks = (compiler) => { - let hooks = compilerHookMap.get(compiler); - if (typeof hooks === 'undefined') { - hooks = { - afterEmit: new SyncWaterfallHook(['manifest']), - beforeEmit: new SyncWaterfallHook(['manifest']) - }; - compilerHookMap.set(compiler, hooks); - } - return hooks; -}; - -const beforeRunHook = ({ emitCountMap, manifestFileName }, compiler, callback) => { - const emitCount = emitCountMap.get(manifestFileName) || 0; - emitCountMap.set(manifestFileName, emitCount + 1); - - /* istanbul ignore next */ - if (callback) { - callback(); - } -}; - -const emitHook = function emit( - { compiler, emitCountMap, manifestAssetId, manifestFileName, moduleAssets, options }, - compilation -) { - const emitCount = emitCountMap.get(manifestFileName) - 1; - // Disable everything we don't use, add asset info, show cached assets - const stats = compilation.getStats().toJson({ - all: false, - assets: true, - cachedAssets: true, - ids: true, - publicPath: true - }); - - const publicPath = options.publicPath !== null ? options.publicPath : stats.publicPath; - const { basePath, removeKeyHash } = options; - - emitCountMap.set(manifestFileName, emitCount); - - const auxiliaryFiles = {}; - let files = Array.from(compilation.chunks).reduce( - (prev, chunk) => reduceChunk(prev, chunk, options, auxiliaryFiles), - [] - ); - - // module assets don't show up in assetsByChunkName, we're getting them this way - files = stats.assets.reduce((prev, asset) => reduceAssets(prev, asset, moduleAssets), files); - - // don't add hot updates and don't add manifests from other instances - files = files.filter( - ({ name, path }) => - !path.includes('hot-update') && - typeof emitCountMap.get(join(compiler.options.output.path, name)) === 'undefined' - ); - - // auxiliary files are "extra" files that are probably already included - // in other ways. Loop over files and remove any from auxiliaryFiles - files.forEach((file) => { - delete auxiliaryFiles[file.path]; - }); - // if there are any auxiliaryFiles left, add them to the files - // this handles, specifically, sourcemaps - Object.keys(auxiliaryFiles).forEach((auxiliaryFile) => { - files = files.concat(auxiliaryFiles[auxiliaryFile]); - }); - - files = files.map((file) => { - const changes = { - // Append optional basepath onto all references. This allows output path to be reflected in the manifest. - name: basePath ? basePath + file.name : file.name, - // Similar to basePath but only affects the value (e.g. how output.publicPath turns - // require('foo/bar') into '/public/foo/bar', see https://github.com/webpack/docs/wiki/configuration#outputpublicpath - path: publicPath ? publicPath + file.path : file.path - }; - - // Fixes #210 - changes.name = removeKeyHash ? changes.name.replace(removeKeyHash, '') : changes.name; - - return Object.assign(file, changes); - }); - - files = transformFiles(files, options); - - let manifest = generateManifest(compilation, files, options); - const isLastEmit = emitCount === 0; - - manifest = getCompilerHooks(compiler).beforeEmit.call(manifest); - - if (isLastEmit) { - const output = options.serialize(manifest); - // - // Object.assign(compilation.assets, { - // [manifestAssetId]: { - // source() { - // return output; - // }, - // size() { - // return output.length; - // } - // } - // }); - // - compilation.emitAsset(manifestAssetId, new RawSource(output)); - - if (options.writeToFileEmit) { - mkdirSync(dirname(manifestFileName), { recursive: true }); - writeFileSync(manifestFileName, output); - } - } - - getCompilerHooks(compiler).afterEmit.call(manifest); -}; - -const normalModuleLoaderHook = ({ moduleAssets }, loaderContext, module) => { - const { emitFile } = loaderContext; - - - loaderContext.emitFile = (file, content, sourceMap, assetInfo) => { - const info = Object.assign({}, assetInfo); - - if (module.userRequest && !moduleAssets[file]) { - info.sourceFilename = join(dirname(file), basename(module.userRequest)); - - Object.assign(moduleAssets, { [file]: info.sourceFilename }); - } - - return emitFile.call(module, file, content, sourceMap, info); - }; -}; - -module.exports = { beforeRunHook, emitHook, getCompilerHooks, normalModuleLoaderHook }; diff --git a/lib/webpack-manifest-plugin/index.js b/lib/webpack-manifest-plugin/index.js deleted file mode 100644 index 4521a3e9..00000000 --- a/lib/webpack-manifest-plugin/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of the Symfony Webpack Encore package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -const { relative, resolve } = require('path'); - -const webpack = require('webpack'); -const NormalModule = require('webpack/lib/NormalModule'); - -const { beforeRunHook, emitHook, getCompilerHooks, normalModuleLoaderHook } = require('./hooks'); - -const emitCountMap = new Map(); - -const defaults = { - basePath: '', - fileName: 'manifest.json', - filter: null, - generate: void 0, - map: null, - publicPath: null, - removeKeyHash: /([a-f0-9]{32}\.?)/gi, - // seed must be reset for each compilation. let the code initialize it to {} - seed: void 0, - serialize(manifest) { - return JSON.stringify(manifest, null, 2); - }, - sort: null, - transformExtensions: /^(gz|map)$/i, - useEntryKeys: false, - writeToFileEmit: false -}; - -class WebpackManifestPlugin { - constructor(opts) { - this.options = Object.assign({}, defaults, opts); - } - - apply(compiler) { - const moduleAssets = {}; - const manifestFileName = resolve(compiler.options.output.path, this.options.fileName); - const manifestAssetId = relative(compiler.options.output.path, manifestFileName); - const beforeRun = beforeRunHook.bind(this, { emitCountMap, manifestFileName }); - const emit = emitHook.bind(this, { - compiler, - emitCountMap, - manifestAssetId, - manifestFileName, - moduleAssets, - options: this.options - }); - const normalModuleLoader = normalModuleLoaderHook.bind(this, { moduleAssets }); - const hookOptions = { - name: 'WebpackManifestPlugin', - stage: Infinity - }; - - compiler.hooks.compilation.tap(hookOptions, (compilation) => { - const hook = !NormalModule.getCompilationHooks - ? compilation.hooks.normalModuleLoader - : NormalModule.getCompilationHooks(compilation).loader; - hook.tap(hookOptions, normalModuleLoader); - }); - - if (webpack.version.startsWith('4')) { - compiler.hooks.emit.tap(hookOptions, emit); - } else { - compiler.hooks.thisCompilation.tap(hookOptions, (compilation) => { - compilation.hooks.processAssets.tap(hookOptions, () => emit(compilation)); - }); - } - - compiler.hooks.run.tap(hookOptions, beforeRun); - compiler.hooks.watchRun.tap(hookOptions, beforeRun); - } -} - -module.exports = { getCompilerHooks, WebpackManifestPlugin }; diff --git a/package.json b/package.json index ee9f935e..cf3be06d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "tapable": "^2.2.1", "terser-webpack-plugin": "^5.3.0", "tmp": "^0.2.1", + "webpack-manifest-plugin": "^5.0.1", "yargs-parser": "^21.0.0" }, "devDependencies": { diff --git a/test/config-generator.js b/test/config-generator.js index 5ec7ef36..caa34eeb 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -14,7 +14,7 @@ const WebpackConfig = require('../lib/WebpackConfig'); const RuntimeConfig = require('../lib/config/RuntimeConfig'); const configGenerator = require('../lib/config-generator'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const { WebpackManifestPlugin } = require('../lib/webpack-manifest-plugin'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); const webpack = require('webpack'); const path = require('path'); const logger = require('../lib/logger'); diff --git a/test/plugins/manifest.js b/test/plugins/manifest.js index fec6eb49..6ad018eb 100644 --- a/test/plugins/manifest.js +++ b/test/plugins/manifest.js @@ -10,7 +10,7 @@ 'use strict'; const expect = require('chai').expect; -const { WebpackManifestPlugin } = require('../../lib/webpack-manifest-plugin'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const manifestPluginUtil = require('../../lib/plugins/manifest'); diff --git a/yarn.lock b/yarn.lock index e2cd3b57..087edeac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7096,6 +7096,11 @@ socks@^2.8.3: ip-address "^9.0.5" smart-buffer "^4.2.0" +source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -7420,6 +7425,11 @@ svgo@^3.3.2: csso "^5.0.5" picocolors "^1.0.0" +tapable@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" + integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== + 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" @@ -7916,6 +7926,14 @@ webpack-dev-server@^5.1.0: webpack-dev-middleware "^7.4.2" ws "^8.18.0" +webpack-manifest-plugin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.1.tgz#8d401fec7fa587cba5af561eb60e39d6a8050e8a" + integrity sha512-xTlX7dC3hrASixA2inuWFMz6qHsNi6MT3Uiqw621sJjRTShtpMjbDYhPPZBwWUKdIYKIjSq9em6+uzWayf38aQ== + dependencies: + tapable "^2.0.0" + webpack-sources "^2.2.0" + webpack-merge@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" @@ -7933,6 +7951,14 @@ webpack-notifier@^1.15.0: node-notifier "^9.0.0" strip-ansi "^6.0.0" +webpack-sources@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + webpack-sources@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723"