mirror of https://github.com/webpack/webpack.git
Merge pull request #11191 from webpack/bugfix/asset-hash
use content hashes for assets
This commit is contained in:
commit
4786a7ec61
|
|
@ -2710,12 +2710,16 @@ Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*
|
|||
let filenameTemplate;
|
||||
/** @type {string} */
|
||||
let file;
|
||||
/** @type {AssetInfo} */
|
||||
let assetInfo;
|
||||
|
||||
let inTry = true;
|
||||
const errorAndCallback = err => {
|
||||
const filename =
|
||||
file ||
|
||||
(typeof filenameTemplate === "string"
|
||||
(typeof file === "string"
|
||||
? file
|
||||
: typeof filenameTemplate === "string"
|
||||
? filenameTemplate
|
||||
: "");
|
||||
|
||||
|
|
@ -2725,13 +2729,18 @@ Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*
|
|||
};
|
||||
|
||||
try {
|
||||
filenameTemplate = fileManifest.filenameTemplate;
|
||||
const pathAndInfo = this.getPathWithInfo(
|
||||
filenameTemplate,
|
||||
fileManifest.pathOptions
|
||||
);
|
||||
file = pathAndInfo.path;
|
||||
const assetInfo = pathAndInfo.info;
|
||||
if ("filename" in fileManifest) {
|
||||
file = fileManifest.filename;
|
||||
assetInfo = fileManifest.info;
|
||||
} else {
|
||||
filenameTemplate = fileManifest.filenameTemplate;
|
||||
const pathAndInfo = this.getPathWithInfo(
|
||||
filenameTemplate,
|
||||
fileManifest.pathOptions
|
||||
);
|
||||
file = pathAndInfo.path;
|
||||
assetInfo = pathAndInfo.info;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return errorAndCallback(err);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const { find } = require("./util/SetHelpers");
|
|||
const { compareModulesById } = require("./util/comparators");
|
||||
|
||||
/** @typedef {import("./Chunk")} Chunk */
|
||||
/** @typedef {import("./Compilation").AssetInfo} AssetInfo */
|
||||
/** @typedef {import("./Compiler")} Compiler */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
|
||||
|
|
@ -454,13 +455,22 @@ class HotModuleReplacementPlugin {
|
|||
chunkGraph
|
||||
});
|
||||
for (const entry of renderManifest) {
|
||||
const {
|
||||
path: filename,
|
||||
info: assetInfo
|
||||
} = compilation.getPathWithInfo(
|
||||
entry.filenameTemplate,
|
||||
entry.pathOptions
|
||||
);
|
||||
/** @type {string} */
|
||||
let filename;
|
||||
/** @type {AssetInfo} */
|
||||
let assetInfo;
|
||||
if ("filename" in entry) {
|
||||
filename = entry.filename;
|
||||
assetInfo = entry.info;
|
||||
} else {
|
||||
({
|
||||
path: filename,
|
||||
info: assetInfo
|
||||
} = compilation.getPathWithInfo(
|
||||
entry.filenameTemplate,
|
||||
entry.pathOptions
|
||||
));
|
||||
}
|
||||
const source = entry.render();
|
||||
compilation.additionalChunkAssets.push(filename);
|
||||
compilation.emitAsset(filename, source, {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,10 @@ const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g;
|
|||
* @property {ChunkGraph} chunkGraph
|
||||
*/
|
||||
|
||||
/** @typedef {RenderManifestEntryTemplated | RenderManifestEntryStatic} RenderManifestEntry */
|
||||
|
||||
/**
|
||||
* @typedef {Object} RenderManifestEntry
|
||||
* @typedef {Object} RenderManifestEntryTemplated
|
||||
* @property {function(): Source} render
|
||||
* @property {string | function(PathData, AssetInfo=): string} filenameTemplate
|
||||
* @property {PathData=} pathOptions
|
||||
|
|
@ -63,6 +65,16 @@ const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g;
|
|||
* @property {boolean=} auxiliary
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RenderManifestEntryStatic
|
||||
* @property {function(): Source} render
|
||||
* @property {string} filename
|
||||
* @property {AssetInfo} info
|
||||
* @property {string} identifier
|
||||
* @property {string=} hash
|
||||
* @property {boolean=} auxiliary
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} HasId
|
||||
* @property {number | string} id
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ const replacePathVariables = (path, data, assetInfo) => {
|
|||
module instanceof Module ? chunkGraph.getModuleId(module) : module.id
|
||||
)
|
||||
);
|
||||
const hashReplacer = hashLength(
|
||||
const moduleHashReplacer = hashLength(
|
||||
replacer(
|
||||
module instanceof Module
|
||||
? chunkGraph.getRenderedModuleHash(module)
|
||||
|
|
@ -234,9 +234,19 @@ const replacePathVariables = (path, data, assetInfo) => {
|
|||
"hashWithLength" in module ? module.hashWithLength : undefined,
|
||||
assetInfo
|
||||
);
|
||||
const contentHashReplacer = hashLength(
|
||||
replacer(data.contentHash),
|
||||
undefined,
|
||||
assetInfo
|
||||
);
|
||||
|
||||
replacements.set("id", idReplacer);
|
||||
replacements.set("hash", hashReplacer);
|
||||
replacements.set("modulehash", moduleHashReplacer);
|
||||
replacements.set("contenthash", contentHashReplacer);
|
||||
replacements.set(
|
||||
"hash",
|
||||
data.contentHash ? contentHashReplacer : moduleHashReplacer
|
||||
);
|
||||
// Legacy
|
||||
replacements.set(
|
||||
"moduleid",
|
||||
|
|
@ -246,14 +256,6 @@ const replacePathVariables = (path, data, assetInfo) => {
|
|||
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_MODULE_ID"
|
||||
)
|
||||
);
|
||||
replacements.set(
|
||||
"modulehash",
|
||||
deprecated(
|
||||
hashReplacer,
|
||||
"[modulehash] is now [hash]",
|
||||
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_MODULE_HASH"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Other things
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const path = require("path");
|
|||
const { RawSource } = require("webpack-sources");
|
||||
const Generator = require("../Generator");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const createHash = require("../util/createHash");
|
||||
|
||||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("../../declarations/plugins/AssetModulesPluginGenerator").AssetModulesPluginGeneratorOptions} AssetModulesPluginGeneratorOptions */
|
||||
|
|
@ -51,9 +52,8 @@ class AssetGenerator extends Generator {
|
|||
default: {
|
||||
runtimeRequirements.add(RuntimeGlobals.module);
|
||||
|
||||
const originalSource = module.originalSource();
|
||||
if (module.buildInfo.dataUrl) {
|
||||
const originalSource = module.originalSource();
|
||||
|
||||
let encodedSource;
|
||||
if (typeof this.dataUrlOptions === "function") {
|
||||
encodedSource = this.dataUrlOptions.call(
|
||||
|
|
@ -108,21 +108,39 @@ class AssetGenerator extends Generator {
|
|||
)};`
|
||||
);
|
||||
} else {
|
||||
const filename = module.nameForCondition();
|
||||
const assetModuleFilename =
|
||||
this.filename || runtimeTemplate.outputOptions.assetModuleFilename;
|
||||
const url = this.compilation.getAssetPath(assetModuleFilename, {
|
||||
const hash = createHash(runtimeTemplate.outputOptions.hashFunction);
|
||||
if (runtimeTemplate.outputOptions.hashSalt) {
|
||||
hash.update(runtimeTemplate.outputOptions.hashSalt);
|
||||
}
|
||||
hash.update(originalSource.buffer());
|
||||
const fullHash = /** @type {string} */ (hash.digest(
|
||||
runtimeTemplate.outputOptions.hashDigest
|
||||
));
|
||||
const contentHash = fullHash.slice(
|
||||
0,
|
||||
runtimeTemplate.outputOptions.hashDigestLength
|
||||
);
|
||||
module.buildInfo.fullContentHash = fullHash;
|
||||
const {
|
||||
path: filename,
|
||||
info
|
||||
} = this.compilation.getAssetPathWithInfo(assetModuleFilename, {
|
||||
module,
|
||||
filename,
|
||||
chunkGraph
|
||||
filename: module.nameForCondition(),
|
||||
chunkGraph,
|
||||
contentHash
|
||||
});
|
||||
module.buildInfo.filename = filename;
|
||||
module.buildInfo.assetInfo = info;
|
||||
|
||||
runtimeRequirements.add(RuntimeGlobals.publicPath); // add __webpack_require__.p
|
||||
|
||||
return new RawSource(
|
||||
`${RuntimeGlobals.module}.exports = ${
|
||||
RuntimeGlobals.publicPath
|
||||
} + ${JSON.stringify(url)};`
|
||||
} + ${JSON.stringify(filename)};`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,9 +148,7 @@ class AssetModulesPlugin {
|
|||
|
||||
compilation.hooks.renderManifest.tap(plugin, (result, options) => {
|
||||
const { chunkGraph } = compilation;
|
||||
const { chunk, runtimeTemplate, codeGenerationResults } = options;
|
||||
|
||||
const { outputOptions } = runtimeTemplate;
|
||||
const { chunk, codeGenerationResults } = options;
|
||||
|
||||
const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
|
||||
chunk,
|
||||
|
|
@ -159,23 +157,14 @@ class AssetModulesPlugin {
|
|||
);
|
||||
if (modules) {
|
||||
for (const module of modules) {
|
||||
const filename = module.nameForCondition();
|
||||
const filenameTemplate =
|
||||
module.buildInfo.assetFilename ||
|
||||
outputOptions.assetModuleFilename;
|
||||
|
||||
result.push({
|
||||
render: () =>
|
||||
codeGenerationResults.get(module).sources.get(type),
|
||||
filenameTemplate,
|
||||
pathOptions: {
|
||||
module,
|
||||
filename,
|
||||
chunkGraph
|
||||
},
|
||||
filename: module.buildInfo.filename,
|
||||
info: module.buildInfo.assetInfo,
|
||||
auxiliary: true,
|
||||
identifier: `assetModule${chunkGraph.getModuleId(module)}`,
|
||||
hash: chunkGraph.getModuleHash(module)
|
||||
hash: module.buildInfo.fullContentHash
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,10 +135,10 @@ exports[`StatsTestCases should print correct stats for asset 1`] = `
|
|||
Time: X ms
|
||||
Built at: 1970-04-20 12:42:42
|
||||
Asset Size
|
||||
89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [name: (main)]
|
||||
bundle.js 10.9 KiB [emitted] [name: main]
|
||||
d896afc62bcc5d86ee78.png 14.6 KiB [emitted] [immutable] [name: (main)]
|
||||
static/file.html 12 bytes [emitted] [name: (main)]
|
||||
Entrypoint main = bundle.js (d896afc62bcc5d86ee78.png static/file.html)
|
||||
Entrypoint main = bundle.js (89a353e9c515885abd8e.png static/file.html)
|
||||
./index.js 150 bytes [built]
|
||||
./images/file.png 42 bytes (javascript) 14.6 KiB (asset) [built]
|
||||
./images/file.svg 915 bytes [built]
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -0,0 +1,6 @@
|
|||
import a from "../_images/file.png";
|
||||
import b from "../_images/file_copy.png";
|
||||
|
||||
it("should use a real content hash for assets", () => {
|
||||
expect(a).toBe(b);
|
||||
});
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
output: {
|
||||
publicPath: "assets/",
|
||||
assetModuleFilename: "file[ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.png$/,
|
||||
type: "asset/resource"
|
||||
}
|
||||
]
|
||||
},
|
||||
experiments: {
|
||||
asset: true
|
||||
}
|
||||
};
|
||||
|
|
@ -1006,7 +1006,10 @@ declare class Compilation {
|
|||
needAdditionalSeal: SyncBailHook<[], boolean>;
|
||||
afterSeal: AsyncSeriesHook<[]>;
|
||||
renderManifest: SyncWaterfallHook<
|
||||
[RenderManifestEntry[], RenderManifestOptions]
|
||||
[
|
||||
(RenderManifestEntryTemplated | RenderManifestEntryStatic)[],
|
||||
RenderManifestOptions
|
||||
]
|
||||
>;
|
||||
fullHash: SyncHook<[Hash], void>;
|
||||
chunkHash: SyncHook<[Chunk, Hash, ChunkHashContext], void>;
|
||||
|
|
@ -1203,7 +1206,9 @@ declare class Compilation {
|
|||
getAsset(name: string): Readonly<Asset>;
|
||||
clearAssets(): void;
|
||||
createModuleAssets(): void;
|
||||
getRenderManifest(options: RenderManifestOptions): RenderManifestEntry[];
|
||||
getRenderManifest(
|
||||
options: RenderManifestOptions
|
||||
): (RenderManifestEntryTemplated | RenderManifestEntryStatic)[];
|
||||
createChunkAssets(callback: (err?: WebpackError) => void): void;
|
||||
getPath(
|
||||
filename: string | ((arg0: PathData, arg1: AssetInfo) => string),
|
||||
|
|
@ -5849,7 +5854,15 @@ declare interface RenderContextObject {
|
|||
*/
|
||||
codeGenerationResults: Map<Module, CodeGenerationResult>;
|
||||
}
|
||||
declare interface RenderManifestEntry {
|
||||
declare interface RenderManifestEntryStatic {
|
||||
render: () => Source;
|
||||
filename: string;
|
||||
info: AssetInfo;
|
||||
identifier: string;
|
||||
hash?: string;
|
||||
auxiliary?: boolean;
|
||||
}
|
||||
declare interface RenderManifestEntryTemplated {
|
||||
render: () => Source;
|
||||
filenameTemplate: string | ((arg0: PathData, arg1: AssetInfo) => string);
|
||||
pathOptions?: PathData;
|
||||
|
|
|
|||
Loading…
Reference in New Issue