Skip to content

Commit

Permalink
feature #1342 Replace assets-webpack-plugin dependency by an intern…
Browse files Browse the repository at this point in the history
…al plugin, to generate `entrypoints.json` file (Kocal)

This PR was merged into the main branch.

Discussion
----------

Replace `assets-webpack-plugin` dependency by an internal plugin, to generate `entrypoints.json` file

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes <!-- please update CHANGELOG.md file -->
| Deprecations? | no <!-- please update CHANGELOG.md file -->
| Issues        | Fix #969 <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
| License       | MIT

This PR replaces the https://github.com/ztoben/assets-webpack-plugin dependency with an internal plugin, this allows us:
- to drop a dependency, and simplify the code (we don't need 10 ~options to generate the `entrypoints.json` for the WebpackEncoreBundle)
- to not depend on https://github.com/waysact/webpack-subresource-integrity, which is necessary to use the `integrity` options of https://github.com/ztoben/assets-webpack-plugin
- to generate the integrity data **after** files have been emitted

**Things done in this PR:**
- Re-implement https://github.com/ztoben/assets-webpack-plugin functionality as closely as possible
- Ensure tests pass without any modifications
- Ensure the branch works like before on a real project (in dev/prod/dev-server, with or without integrity hashes)

**Things not done in this PR:**
- #1269 will have to wait for another PR, I don't wanted to fix that here

Commits
-------

0529865 Replace `assets-webpack-plugin` dependency by an internal plugin, to generate `entrypoints.json` file
  • Loading branch information
Kocal committed Sep 20, 2024
2 parents 10e9a96 + 0529865 commit b212b15
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 87 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 6 additions & 76 deletions lib/plugins/entry-files-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
});
};
2 changes: 1 addition & 1 deletion lib/plugins/plugin-priorities.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ module.exports = {
FriendlyErrorsWebpackPlugin: 40,
AssetOutputDisplayPlugin: 30,
ForkTsCheckerWebpackPlugin: 10,
AssetsPlugin: -10,
EntryPointsPlugin: -10,
};
136 changes: 136 additions & 0 deletions lib/webpack/entry-points-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* This file is part of the Symfony Webpack Encore package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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<string>} 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 };
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 0 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit b212b15

Please sign in to comment.