mirror of https://github.com/webpack/webpack.git
fix asset module hash
This commit is contained in:
parent
f1e9221da6
commit
8d8e2e3fa4
|
@ -36,6 +36,7 @@
|
|||
* @property {NormalModule} module the module
|
||||
* @property {ChunkGraph} chunkGraph
|
||||
* @property {RuntimeSpec} runtime
|
||||
* @property {RuntimeTemplate=} runtimeTemplate
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,6 +82,7 @@ class RuntimeTemplate {
|
|||
this.outputOptions = outputOptions || {};
|
||||
this.requestShortener = requestShortener;
|
||||
this.globalObject = getGlobalObject(outputOptions.globalObject);
|
||||
this.contentHashReplacement = "X".repeat(outputOptions.hashDigestLength);
|
||||
}
|
||||
|
||||
isIIFE() {
|
||||
|
|
|
@ -76,6 +76,7 @@ const mergeRelatedInfo = (a, b) => {
|
|||
|
||||
const JS_TYPES = new Set(["javascript"]);
|
||||
const JS_AND_ASSET_TYPES = new Set(["javascript", "asset"]);
|
||||
const DEFAULT_ENCODING = "base64";
|
||||
|
||||
class AssetGenerator extends Generator {
|
||||
/**
|
||||
|
@ -92,6 +93,44 @@ class AssetGenerator extends Generator {
|
|||
this.emit = emit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NormalModule} module module
|
||||
* @param {RuntimeTemplate} runtimeTemplate runtime template
|
||||
* @returns {string} source file name
|
||||
*/
|
||||
getSourceFileName(module, runtimeTemplate) {
|
||||
return makePathsRelative(
|
||||
runtimeTemplate.compilation.compiler.context,
|
||||
module.matchResource || module.resource,
|
||||
runtimeTemplate.compilation.compiler.root
|
||||
).replace(/^\.\//, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NormalModule} module module
|
||||
* @returns {string|undefined} mime type
|
||||
*/
|
||||
getMimeType(module) {
|
||||
if (typeof this.dataUrlOptions === "function") return;
|
||||
|
||||
let mimeType = this.dataUrlOptions.mimetype;
|
||||
if (mimeType === undefined) {
|
||||
let ext = path.extname(module.nameForCondition());
|
||||
if (
|
||||
module.resourceResolveData &&
|
||||
module.resourceResolveData.mimetype !== undefined
|
||||
) {
|
||||
mimeType =
|
||||
module.resourceResolveData.mimetype +
|
||||
module.resourceResolveData.parameters;
|
||||
} else if (ext) {
|
||||
mimeType = mimeTypes.lookup(ext);
|
||||
}
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NormalModule} module module for which the code should be generated
|
||||
* @param {GenerateContext} generateContext context for generate
|
||||
|
@ -131,27 +170,15 @@ class AssetGenerator extends Generator {
|
|||
}
|
||||
}
|
||||
if (encoding === undefined) {
|
||||
encoding = "base64";
|
||||
}
|
||||
let ext;
|
||||
let mimeType = this.dataUrlOptions.mimetype;
|
||||
if (mimeType === undefined) {
|
||||
ext = path.extname(module.nameForCondition());
|
||||
if (
|
||||
module.resourceResolveData &&
|
||||
module.resourceResolveData.mimetype !== undefined
|
||||
) {
|
||||
mimeType =
|
||||
module.resourceResolveData.mimetype +
|
||||
module.resourceResolveData.parameters;
|
||||
} else if (ext) {
|
||||
mimeType = mimeTypes.lookup(ext);
|
||||
}
|
||||
encoding = DEFAULT_ENCODING;
|
||||
}
|
||||
const mimeType = this.getMimeType(module);
|
||||
if (typeof mimeType !== "string") {
|
||||
throw new Error(
|
||||
"DataUrl can't be generated automatically, " +
|
||||
`because there is no mimetype for "${ext}" in mimetype database. ` +
|
||||
`because there is no mimetype for "${path.extname(
|
||||
module.nameForCondition()
|
||||
)}" in mimetype database. ` +
|
||||
'Either pass a mimetype via "generator.mimetype" or ' +
|
||||
'use type: "asset/resource" to create a resource file instead of a DataUrl'
|
||||
);
|
||||
|
@ -212,11 +239,10 @@ class AssetGenerator extends Generator {
|
|||
runtimeTemplate.outputOptions.hashDigestLength
|
||||
);
|
||||
module.buildInfo.fullContentHash = fullHash;
|
||||
const sourceFilename = makePathsRelative(
|
||||
runtimeTemplate.compilation.compiler.context,
|
||||
module.matchResource || module.resource,
|
||||
runtimeTemplate.compilation.compiler.root
|
||||
).replace(/^\.\//, "");
|
||||
const sourceFilename = this.getSourceFileName(
|
||||
module,
|
||||
runtimeTemplate
|
||||
);
|
||||
let { path: filename, info: assetInfo } =
|
||||
runtimeTemplate.compilation.getAssetPathWithInfo(
|
||||
assetModuleFilename,
|
||||
|
@ -327,8 +353,44 @@ class AssetGenerator extends Generator {
|
|||
* @param {Hash} hash hash that will be modified
|
||||
* @param {UpdateHashContext} updateHashContext context for updating hash
|
||||
*/
|
||||
updateHash(hash, { module }) {
|
||||
hash.update(module.buildInfo.dataUrl ? "data-url" : "resource");
|
||||
updateHash(hash, { module, runtime, runtimeTemplate, chunkGraph }) {
|
||||
if (module.buildInfo.dataUrl) {
|
||||
hash.update("data-url");
|
||||
if (typeof this.dataUrlOptions === "function") {
|
||||
hash.update("unknown-encoding");
|
||||
hash.update("unknown-mimetype");
|
||||
} else {
|
||||
hash.update(this.dataUrlOptions.encoding || DEFAULT_ENCODING);
|
||||
hash.update(this.getMimeType(module) || "unknown-mimetype");
|
||||
}
|
||||
} else {
|
||||
hash.update("resource");
|
||||
|
||||
const pathData = {
|
||||
module,
|
||||
runtime,
|
||||
filename: this.getSourceFileName(module, runtimeTemplate),
|
||||
chunkGraph,
|
||||
contentHash: runtimeTemplate.contentHashReplacement
|
||||
};
|
||||
|
||||
if (typeof this.publicPath === "function") {
|
||||
hash.update(`path:${this.publicPath(pathData, {})}`);
|
||||
} else if (this.publicPath) {
|
||||
hash.update(`path:${this.publicPath}`);
|
||||
} else {
|
||||
hash.update("no-path");
|
||||
}
|
||||
|
||||
const assetModuleFilename =
|
||||
this.filename || runtimeTemplate.outputOptions.assetModuleFilename;
|
||||
const { path: filename } =
|
||||
runtimeTemplate.compilation.getAssetPathWithInfo(
|
||||
assetModuleFilename,
|
||||
pathData
|
||||
);
|
||||
hash.update(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2493,7 +2493,7 @@ exports[`StatsTestCases should print correct stats for real-content-hash 1`] = `
|
|||
|
||||
b-normal:
|
||||
assets by path *.js 3.23 KiB
|
||||
asset 415ca114e6dbdbc5f84c-415ca1.js 2.75 KiB [emitted] [immutable] [minimized] (name: runtime)
|
||||
asset e1e88dc9cfc1b47c4954-e1e88d.js 2.75 KiB [emitted] [immutable] [minimized] (name: runtime)
|
||||
asset a6d438a0676f93383d79-a6d438.js 262 bytes [emitted] [immutable] [minimized] (name: lazy)
|
||||
asset cbb9c74e42f00ada40f7-cbb9c7.js 212 bytes [emitted] [immutable] [minimized] (name: index)
|
||||
asset 666f2b8847021ccc7608-666f2b.js 21 bytes [emitted] [immutable] [minimized] (name: a, b)
|
||||
|
@ -2501,7 +2501,7 @@ b-normal:
|
|||
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: file.png] (auxiliary name: lazy)
|
||||
asset 7382fad5b015914e0811.jpg?query 5.89 KiB [cached] [immutable] [from: file.jpg?query] (auxiliary name: lazy)
|
||||
asset 7382fad5b015914e0811.jpg 5.89 KiB [emitted] [immutable] [from: file.jpg] (auxiliary name: index)
|
||||
Entrypoint index 2.96 KiB (5.89 KiB) = 415ca114e6dbdbc5f84c-415ca1.js 2.75 KiB cbb9c74e42f00ada40f7-cbb9c7.js 212 bytes 1 auxiliary asset
|
||||
Entrypoint index 2.96 KiB (5.89 KiB) = e1e88dc9cfc1b47c4954-e1e88d.js 2.75 KiB cbb9c74e42f00ada40f7-cbb9c7.js 212 bytes 1 auxiliary asset
|
||||
Entrypoint a 21 bytes = 666f2b8847021ccc7608-666f2b.js
|
||||
Entrypoint b 21 bytes = 666f2b8847021ccc7608-666f2b.js
|
||||
runtime modules 7.29 KiB 9 modules
|
||||
|
@ -2520,8 +2520,8 @@ b-normal:
|
|||
|
||||
a-source-map:
|
||||
assets by path *.js 3.45 KiB
|
||||
asset d618b31bb631bdbaee8e-d618b3.js 2.81 KiB [emitted] [immutable] [minimized] (name: runtime)
|
||||
sourceMap d618b31bb631bdbaee8e-d618b3.js.map 14.5 KiB [emitted] [dev] (auxiliary name: runtime)
|
||||
asset 3d558d1fe89c5b37fabe-3d558d.js 2.81 KiB [emitted] [immutable] [minimized] (name: runtime)
|
||||
sourceMap 3d558d1fe89c5b37fabe-3d558d.js.map 14.5 KiB [emitted] [dev] (auxiliary name: runtime)
|
||||
asset da6ceedb86c86e79a49a-da6cee.js 318 bytes [emitted] [immutable] [minimized] (name: lazy)
|
||||
sourceMap da6ceedb86c86e79a49a-da6cee.js.map 401 bytes [emitted] [dev] (auxiliary name: lazy)
|
||||
asset 9e0ae6ff74fb2c3c821b-9e0ae6.js 268 bytes [emitted] [immutable] [minimized] (name: index)
|
||||
|
@ -2532,7 +2532,7 @@ a-source-map:
|
|||
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: file.png] (auxiliary name: lazy)
|
||||
asset 7382fad5b015914e0811.jpg?query 5.89 KiB [cached] [immutable] [from: file.jpg?query] (auxiliary name: lazy)
|
||||
asset 7382fad5b015914e0811.jpg 5.89 KiB [emitted] [immutable] [from: file.jpg] (auxiliary name: index)
|
||||
Entrypoint index 3.07 KiB (20.7 KiB) = d618b31bb631bdbaee8e-d618b3.js 2.81 KiB 9e0ae6ff74fb2c3c821b-9e0ae6.js 268 bytes 3 auxiliary assets
|
||||
Entrypoint index 3.07 KiB (20.7 KiB) = 3d558d1fe89c5b37fabe-3d558d.js 2.81 KiB 9e0ae6ff74fb2c3c821b-9e0ae6.js 268 bytes 3 auxiliary assets
|
||||
Entrypoint a 77 bytes (254 bytes) = 222c2acc68675174e6b2-222c2a.js 1 auxiliary asset
|
||||
Entrypoint b 77 bytes (254 bytes) = 222c2acc68675174e6b2-222c2a.js 1 auxiliary asset
|
||||
runtime modules 7.29 KiB 9 modules
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import img from "./1.jpg";
|
||||
|
||||
it("should compile", () => {
|
||||
expect(typeof img).toBe("string");
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
const findOutputFiles = require("../../../helpers/findOutputFiles");
|
||||
|
||||
const allAssets = new Set();
|
||||
const allBundles = new Set();
|
||||
|
||||
module.exports = {
|
||||
findBundle: function(i, options) {
|
||||
const bundle = findOutputFiles(options, new RegExp(`^bundle${i}`))[0];
|
||||
allBundles.add(/\.([^.]+)\./.exec(bundle)[1]);
|
||||
|
||||
let asset;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
asset = findOutputFiles(options, /^1\.[^\.]*\.jpg$/, 'img')[0];
|
||||
break;
|
||||
case 1:
|
||||
case 5:
|
||||
asset = findOutputFiles(options, /^1\.[^\.]*\.jpg$/, 'asset')[0];
|
||||
break;
|
||||
}
|
||||
|
||||
if (asset) allAssets.add(asset);
|
||||
|
||||
return `./${bundle}`;
|
||||
},
|
||||
afterExecute: () => {
|
||||
expect(allBundles.size).toBe(6);
|
||||
expect(allAssets.size).toBe(1);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,118 @@
|
|||
/** @type {import("../../../../").Configuration[]} */
|
||||
module.exports = [
|
||||
{
|
||||
output: {
|
||||
filename: "bundle0.[contenthash].js",
|
||||
assetModuleFilename: "img/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle1.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle2.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/resource",
|
||||
generator: {
|
||||
publicPath: "/public/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle3.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/inline"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle4.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/inline",
|
||||
generator: {
|
||||
dataUrl: {
|
||||
encoding: false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle5.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/source",
|
||||
generator: {
|
||||
dataUrl: {
|
||||
mimetype: "text/plain"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
output: {
|
||||
filename: "bundle6.[contenthash].js",
|
||||
assetModuleFilename: "asset/[name].[contenthash][ext]"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
type: "asset/resource",
|
||||
generator: {
|
||||
// should result in same hash as bundle2
|
||||
publicPath: () => "/public/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
|
@ -1,25 +1,15 @@
|
|||
var fs = require("fs");
|
||||
|
||||
var findFile = function(files, regex) {
|
||||
return files.find(function(file) {
|
||||
if (regex.test(file)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
const findOutputFiles = require("../../../helpers/findOutputFiles");
|
||||
|
||||
const allFilenameHashes = new Set();
|
||||
const allChunkHashes = new Set();
|
||||
|
||||
module.exports = {
|
||||
findBundle: function(i, options) {
|
||||
var files = fs.readdirSync(options.output.path);
|
||||
|
||||
const filename = findFile(files, new RegExp(`^bundle${i}`));
|
||||
const filename = findOutputFiles(options, new RegExp(`^bundle${i}`))[0];
|
||||
const filenameHash = /\.([a-f0-9]+)\.js$/.exec(filename)[1];
|
||||
allFilenameHashes.add(filenameHash);
|
||||
|
||||
const chunk = findFile(files, new RegExp(`^chunk${i}`));
|
||||
const chunk = findOutputFiles(options, new RegExp(`^chunk${i}`))[0];
|
||||
const chunkHash = /\.([a-f0-9]+)\.js$/.exec(chunk)[1];
|
||||
allChunkHashes.add(chunkHash);
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
* @param {{output: {path: string}}} options options
|
||||
* @param {RegExp} regexp regexp
|
||||
* @param {string=} subpath path in output directory
|
||||
* @returns {string[]} files
|
||||
*/
|
||||
module.exports = function findOutputFiles(options, regexp, subpath) {
|
||||
const files = fs.readdirSync(
|
||||
subpath ? path.join(options.output.path, subpath) : options.output.path
|
||||
);
|
||||
|
||||
return files.filter(file => regexp.test(file));
|
||||
};
|
|
@ -10023,6 +10023,7 @@ declare abstract class RuntimeTemplate {
|
|||
outputOptions: OutputNormalized;
|
||||
requestShortener: RequestShortener;
|
||||
globalObject: string;
|
||||
contentHashReplacement: string;
|
||||
isIIFE(): undefined | boolean;
|
||||
isModule(): undefined | boolean;
|
||||
supportsConst(): undefined | boolean;
|
||||
|
@ -11506,6 +11507,7 @@ declare interface UpdateHashContextGenerator {
|
|||
module: NormalModule;
|
||||
chunkGraph: ChunkGraph;
|
||||
runtime: RuntimeSpec;
|
||||
runtimeTemplate?: RuntimeTemplate;
|
||||
}
|
||||
type UsageStateType = 0 | 1 | 2 | 3 | 4;
|
||||
declare interface UserResolveOptions {
|
||||
|
|
Loading…
Reference in New Issue