Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <[email protected]>
*
* 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) {
Kocal marked this conversation as resolved.
Show resolved Hide resolved
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;
}
Comment on lines +74 to +77
Copy link
Member Author

@Kocal Kocal Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you update a file while the dev-server is running, an .hot-update. file can be emitted, but we don't want to add it in the manifest.


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==

[email protected].*:
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