Skip to content

Commit 2fc4c8d

Browse files
committed
Add 'outputPath' configuration option for resource asset modules
1 parent 10b38b3 commit 2fc4c8d

13 files changed

Lines changed: 131 additions & 12 deletions

File tree

declarations/WebpackOptions.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,15 @@ export type AssetGeneratorDataUrlFunction = (
692692
*/
693693
export type AssetGeneratorOptions = AssetInlineGeneratorOptions &
694694
AssetResourceGeneratorOptions;
695+
/**
696+
* Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.
697+
*/
698+
export type AssetModuleOutputPath =
699+
| string
700+
| ((
701+
pathData: import("../lib/Compilation").PathData,
702+
assetInfo?: import("../lib/Compilation").AssetInfo
703+
) => string);
695704
/**
696705
* Function that executes for module and should return whenever asset should be inlined as DataUrl.
697706
*/
@@ -2705,6 +2714,10 @@ export interface AssetResourceGeneratorOptions {
27052714
* Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
27062715
*/
27072716
filename?: FilenameTemplate;
2717+
/**
2718+
* Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.
2719+
*/
2720+
outputPath?: AssetModuleOutputPath;
27082721
/**
27092722
* The 'publicPath' specifies the public URL address of the output files when referenced in a browser.
27102723
*/

lib/asset/AssetGenerator.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { makePathsRelative } = require("../util/identifier");
1515

1616
/** @typedef {import("webpack-sources").Source} Source */
1717
/** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
18+
/** @typedef {import("../../declarations/WebpackOptions").AssetModuleOutputPath} AssetModuleOutputPath */
1819
/** @typedef {import("../../declarations/WebpackOptions").RawPublicPath} RawPublicPath */
1920
/** @typedef {import("../Compilation")} Compilation */
2021
/** @typedef {import("../Compiler")} Compiler */
@@ -117,13 +118,15 @@ class AssetGenerator extends Generator {
117118
* @param {AssetGeneratorOptions["dataUrl"]=} dataUrlOptions the options for the data url
118119
* @param {string=} filename override for output.assetModuleFilename
119120
* @param {RawPublicPath=} publicPath override for output.assetModulePublicPath
121+
* @param {AssetModuleOutputPath=} outputPath the output path for the emitted file which is not included in the runtime import
120122
* @param {boolean=} emit generate output asset
121123
*/
122-
constructor(dataUrlOptions, filename, publicPath, emit) {
124+
constructor(dataUrlOptions, filename, publicPath, outputPath, emit) {
123125
super();
124126
this.dataUrlOptions = dataUrlOptions;
125127
this.filename = filename;
126128
this.publicPath = publicPath;
129+
this.outputPath = outputPath;
127130
this.emit = emit;
128131
}
129132

@@ -276,15 +279,31 @@ class AssetGenerator extends Generator {
276279
sourceFilename,
277280
...assetInfo
278281
};
279-
module.buildInfo.filename = filename;
282+
let outputFilename = filename;
283+
if (this.outputPath) {
284+
const { path: outputPath, info } =
285+
runtimeTemplate.compilation.getAssetPathWithInfo(
286+
this.outputPath,
287+
{
288+
module,
289+
runtime,
290+
filename: sourceFilename,
291+
chunkGraph,
292+
contentHash
293+
}
294+
);
295+
assetInfo = mergeAssetInfo(assetInfo, info);
296+
outputFilename = path.posix.join(outputPath, filename);
297+
}
298+
module.buildInfo.filename = outputFilename;
280299
module.buildInfo.assetInfo = assetInfo;
281300
if (getData) {
282301
// Due to code generation caching module.buildInfo.XXX can't used to store such information
283302
// It need to be stored in the code generation results instead, where it's cached too
284303
// TODO webpack 6 For back-compat reasons we also store in on module.buildInfo
285304
const data = getData();
286305
data.set("fullContentHash", fullHash);
287-
data.set("filename", filename);
306+
data.set("filename", outputFilename);
288307
data.set("assetInfo", assetInfo);
289308
}
290309

lib/asset/AssetModulesPlugin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,11 @@ class AssetModulesPlugin {
137137

138138
let filename = undefined;
139139
let publicPath = undefined;
140+
let outputPath = undefined;
140141
if (type !== "asset/inline") {
141142
filename = generatorOptions.filename;
142143
publicPath = generatorOptions.publicPath;
144+
outputPath = generatorOptions.outputPath;
143145
}
144146

145147
const AssetGenerator = getAssetGenerator();
@@ -148,6 +150,7 @@ class AssetModulesPlugin {
148150
dataUrl,
149151
filename,
150152
publicPath,
153+
outputPath,
151154
generatorOptions.emit !== false
152155
);
153156
});

schemas/WebpackOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemas/WebpackOptions.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@
108108
"filename": {
109109
"$ref": "#/definitions/FilenameTemplate"
110110
},
111+
"outputPath": {
112+
"$ref": "#/definitions/AssetModuleOutputPath"
113+
},
111114
"publicPath": {
112115
"$ref": "#/definitions/RawPublicPath"
113116
}
@@ -136,6 +139,19 @@
136139
}
137140
]
138141
},
142+
"AssetModuleOutputPath": {
143+
"description": "Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.",
144+
"anyOf": [
145+
{
146+
"type": "string",
147+
"absolutePath": false
148+
},
149+
{
150+
"instanceof": "Function",
151+
"tsType": "((pathData: import(\"../lib/Compilation\").PathData, assetInfo?: import(\"../lib/Compilation\").AssetInfo) => string)"
152+
}
153+
]
154+
},
139155
"AssetParserDataUrlFunction": {
140156
"description": "Function that executes for module and should return whenever asset should be inlined as DataUrl.",
141157
"instanceof": "Function",
@@ -182,6 +198,9 @@
182198
"filename": {
183199
"$ref": "#/definitions/FilenameTemplate"
184200
},
201+
"outputPath": {
202+
"$ref": "#/definitions/AssetModuleOutputPath"
203+
},
185204
"publicPath": {
186205
"$ref": "#/definitions/RawPublicPath"
187206
}

schemas/plugins/asset/AssetGeneratorOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemas/plugins/asset/AssetResourceGeneratorOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/__snapshots__/Cli.basictest.js.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,19 @@ Object {
12831283
"multiple": false,
12841284
"simpleType": "string",
12851285
},
1286+
"module-generator-asset-output-path": Object {
1287+
"configs": Array [
1288+
Object {
1289+
"description": "Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.",
1290+
"multiple": false,
1291+
"path": "module.generator.asset.outputPath",
1292+
"type": "string",
1293+
},
1294+
],
1295+
"description": "Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.",
1296+
"multiple": false,
1297+
"simpleType": "string",
1298+
},
12861299
"module-generator-asset-public-path": Object {
12871300
"configs": Array [
12881301
Object {
@@ -1322,6 +1335,19 @@ Object {
13221335
"multiple": false,
13231336
"simpleType": "string",
13241337
},
1338+
"module-generator-asset-resource-output-path": Object {
1339+
"configs": Array [
1340+
Object {
1341+
"description": "Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.",
1342+
"multiple": false,
1343+
"path": "module.generator.asset/resource.outputPath",
1344+
"type": "string",
1345+
},
1346+
],
1347+
"description": "Emit the asset in the specified folder instead relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.",
1348+
"multiple": false,
1349+
"simpleType": "string",
1350+
},
13251351
"module-generator-asset-resource-public-path": Object {
13261352
"configs": Array [
13271353
Object {
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import url from "../_images/file.png";
2+
import url2 from "../_images/file.jpg";
23
import fs from "fs";
4+
import path from "path";
35

46
it("should output asset with path", () => {
57
expect(url).toEqual("images/file.png");
6-
expect(() => fs.statSync(url)).toThrowError(
7-
expect.objectContaining({
8-
code: "ENOENT"
9-
})
10-
);
8+
expect(url2).toEqual("images/file.jpg");
9+
10+
expect(fs.existsSync(path.join(__STATS__.outputPath, url))).toBe(false);
11+
expect(fs.existsSync(path.join(__STATS__.outputPath, url2))).toBe(true);
1112
});

test/configCases/asset-modules/emit/webpack.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ module.exports = {
88
rules: [
99
{
1010
test: /\.png$/,
11-
type: "asset",
11+
type: "asset/resource",
1212
generator: {
1313
emit: false
1414
}
15+
},
16+
{
17+
test: /\.jpg$/,
18+
type: "asset/resource"
1519
}
1620
]
1721
}

0 commit comments

Comments
 (0)