-
-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #1342 Replace
assets-webpack-plugin
dependency by an intern…
…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
Showing
6 changed files
with
145 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters