From 05298654d002b17698b907c43ce7c334dc108e2b Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 17 Sep 2024 21:23:45 +0200 Subject: [PATCH] Replace `assets-webpack-plugin` dependency by an internal plugin, to generate `entrypoints.json` file --- CHANGELOG.md | 2 + lib/plugins/entry-files-manifest.js | 82 ++--------------- lib/plugins/plugin-priorities.js | 2 +- lib/webpack/entry-points-plugin.js | 136 ++++++++++++++++++++++++++++ package.json | 1 - yarn.lock | 9 -- 6 files changed, 145 insertions(+), 87 deletions(-) create mode 100644 lib/webpack/entry-points-plugin.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f6af9886..599e418d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,8 @@ yarn add webpack-dev-server --dev pnpm install webpack-dev-server --save-dev ``` +* #1342 Replace [`assets-webpack-plugin`](https://github.com/ztoben/assets-webpack-plugin) dependency by an internal plugin, to generate `entrypoints.json` file (@Kocal) + ## 4.7.0 ### Features diff --git a/lib/plugins/entry-files-manifest.js b/lib/plugins/entry-files-manifest.js index 4fbe1a66..191bddc7 100644 --- a/lib/plugins/entry-files-manifest.js +++ b/lib/plugins/entry-files-manifest.js @@ -14,75 +14,7 @@ */ const PluginPriorities = require('./plugin-priorities'); -const copyEntryTmpName = require('../utils/copyEntryTmpName'); -const AssetsPlugin = require('assets-webpack-plugin'); -const fs = require('fs'); -const path = require('path'); -const crypto = require('crypto'); - -function processOutput(webpackConfig) { - return (assets) => { - // Remove temporary entry added by the copyFiles feature - delete assets[copyEntryTmpName]; - - // with --watch or dev-server, subsequent calls will include - // the original assets (so, assets.entrypoints) + the new - // assets (which will have their original structure). We - // delete the entrypoints key, and then process the new assets - // like normal below. The same reasoning applies to the - // integrity key. - delete assets.entrypoints; - delete assets.integrity; - - // This will iterate over all the entry points and convert the - // one file entries into an array of one entry since that was how the entry point file was before this change. - const integrity = {}; - const integrityAlgorithms = webpackConfig.integrityAlgorithms; - const publicPath = webpackConfig.getRealPublicPath(); - - for (const asset in assets) { - for (const fileType in assets[asset]) { - if (!Array.isArray(assets[asset][fileType])) { - assets[asset][fileType] = [assets[asset][fileType]]; - } - - if (integrityAlgorithms.length) { - for (const file of assets[asset][fileType]) { - if (file in integrity) { - continue; - } - - const filePath = path.resolve( - webpackConfig.outputPath, - file.replace(publicPath, '') - ); - - if (fs.existsSync(filePath)) { - const fileHashes = []; - - for (const algorithm of webpackConfig.integrityAlgorithms) { - const hash = crypto.createHash(algorithm); - const fileContent = fs.readFileSync(filePath, 'utf8'); - hash.update(fileContent, 'utf8'); - - fileHashes.push(`${algorithm}-${hash.digest('base64')}`); - } - - integrity[file] = fileHashes.join(' '); - } - } - } - } - } - - const manifestContent = { entrypoints: assets }; - if (integrityAlgorithms.length) { - manifestContent.integrity = integrity; - } - - return JSON.stringify(manifestContent, null, 2); - }; -} +const { EntryPointsPlugin } = require('../webpack/entry-points-plugin'); /** * @param {Array} plugins @@ -91,13 +23,11 @@ function processOutput(webpackConfig) { */ module.exports = function(plugins, webpackConfig) { plugins.push({ - plugin: new AssetsPlugin({ - path: webpackConfig.outputPath, - filename: 'entrypoints.json', - includeAllFileTypes: true, - entrypoints: true, - processOutput: processOutput(webpackConfig) + plugin: new EntryPointsPlugin({ + publicPath: webpackConfig.getRealPublicPath(), + outputPath: webpackConfig.outputPath, + integrityAlgorithms: webpackConfig.integrityAlgorithms }), - priority: PluginPriorities.AssetsPlugin + priority: PluginPriorities.EntryPointsPlugin }); }; diff --git a/lib/plugins/plugin-priorities.js b/lib/plugins/plugin-priorities.js index 89a4d6c7..ce0be759 100644 --- a/lib/plugins/plugin-priorities.js +++ b/lib/plugins/plugin-priorities.js @@ -21,5 +21,5 @@ module.exports = { FriendlyErrorsWebpackPlugin: 40, AssetOutputDisplayPlugin: 30, ForkTsCheckerWebpackPlugin: 10, - AssetsPlugin: -10, + EntryPointsPlugin: -10, }; diff --git a/lib/webpack/entry-points-plugin.js b/lib/webpack/entry-points-plugin.js new file mode 100644 index 00000000..59a538e0 --- /dev/null +++ b/lib/webpack/entry-points-plugin.js @@ -0,0 +1,136 @@ +/* + * 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. + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); +const copyEntryTmpName = require('../utils/copyEntryTmpName'); + +/** + * Return the file extension from a filename, without the leading dot and without the query string (if any). + * + * @param {string} filename + * @returns {string} + */ +function getFileExtension(filename) { + return path.extname(filename).slice(1).split('?')[0]; +} + +class EntryPointsPlugin { + /** + * @param {object} options + * @param {string} options.publicPath The public path of the assets, from where they are served + * @param {string} options.outputPath The output path of the assets, from where they are saved + * @param {Array} options.integrityAlgorithms The algorithms to use for the integrity hash + */ + constructor({ + publicPath, + outputPath, + integrityAlgorithms + }) { + this.publicPath = publicPath; + this.outputPath = outputPath; + this.integrityAlgorithms = integrityAlgorithms; + } + + /** + * @param {import('webpack').Compiler} compiler + */ + apply(compiler) { + compiler.hooks.afterEmit.tapAsync({ name: 'EntryPointsPlugin' }, (compilation, callback) => { + const manifest = { + entrypoints: {}, + }; + + const stats = compilation.getStats().toJson({ + assets: true, + moduleAssets: true, + relatedAssets: false, + chunkGroupAuxiliary: false, + chunks: false, + modules: false, + timings: false, + logging: false, + errorDetails: false, + }); + + for (const [entryName, entry] of Object.entries(stats.entrypoints)) { + // We don't want to include the temporary entry in the manifest + if (entryName === copyEntryTmpName) { + continue; + } + + manifest.entrypoints[entryName] = {}; + + for (const asset of entry.assets) { + // We don't want to include hot-update files in the manifest + if (asset.name.includes('.hot-update.')) { + continue; + } + + const fileExtension = getFileExtension(asset.name); + const assetPath = this.publicPath.slice(-1) === '/' + ? `${this.publicPath}${asset.name}` + : `${this.publicPath}/${asset.name}`; + + if (!(fileExtension in manifest.entrypoints[entryName])) { + manifest.entrypoints[entryName][fileExtension] = []; + } + + manifest.entrypoints[entryName][fileExtension].push(assetPath); + } + } + + if (this.integrityAlgorithms.length > 0) { + manifest.integrity = {}; + + for (const entryName in manifest.entrypoints) { + for (const fileType in manifest.entrypoints[entryName]) { + for (const asset of manifest.entrypoints[entryName][fileType]) { + if (asset in manifest.integrity) { + continue; + } + + const filePath = path.resolve( + this.outputPath, + asset.replace(this.publicPath, ''), + ); + + if (fs.existsSync(filePath)) { + const fileHashes = []; + + for (const algorithm of this.integrityAlgorithms) { + const hash = crypto.createHash(algorithm); + const fileContent = fs.readFileSync(filePath, 'utf8'); + hash.update(fileContent, 'utf8'); + + fileHashes.push(`${algorithm}-${hash.digest('base64')}`); + } + + manifest.integrity[asset] = fileHashes.join(' '); + } + } + } + } + } + + fs.writeFileSync( + path.join(this.outputPath, 'entrypoints.json'), + JSON.stringify(manifest, null, 2), + { flag: 'w' }, + ); + + callback(); + }); + } +} + +module.exports = { EntryPointsPlugin }; diff --git a/package.json b/package.json index 58647bcc..24e97b07 100755 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "homepage": "https://github.com/symfony/webpack-encore", "dependencies": { "@nuxt/friendly-errors-webpack-plugin": "^2.5.1", - "assets-webpack-plugin": "7.0.*", "babel-loader": "^9.1.3", "css-loader": "^6.7.0", "css-minimizer-webpack-plugin": "^7.0.0", diff --git a/yarn.lock b/yarn.lock index 2341150a..a4109172 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2091,15 +2091,6 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -assets-webpack-plugin@7.0.*: - version "7.0.0" - resolved "https://registry.yarnpkg.com/assets-webpack-plugin/-/assets-webpack-plugin-7.0.0.tgz#c61ed7466f35ff7a4d90d7070948736f471b8804" - integrity sha512-DMZ9r6HFxynWeONRMhSOFTvTrmit5dovdoUKdJgCG03M6CC7XiwNImPH+Ad1jaVrQ2n59e05lBhte52xPt4MSA== - dependencies: - camelcase "^6.0.0" - escape-string-regexp "^4.0.0" - lodash "^4.17.20" - ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782"