Skip to content

Commit 9cdcc57

Browse files
fix(target): report "universal" as the loader target (#21540)
* fix(target): report "universal" as the loader target `options.loader.target` fell through to `undefined` for the universal target (`"universal"` / `["web", "node"]` + ESM output), so loaders had no way to tell what they were building for. Also extend the universal coverage across module types: css/auto, css in an async chunk, `data:` uri modules and the loader target in `target/universal-all-module-types`, plus a new `target/universal-html-entry` case covering an html entry with its webmanifest, assets and extracted css. * test(target): type the loader context in the universal target loader
1 parent 774e7e4 commit 9cdcc57

20 files changed

Lines changed: 212 additions & 4 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": patch
3+
---
4+
5+
Report `"universal"` as the loader context target for the universal target.

lib/config/defaults.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,13 @@ const applyWebpackOptionsDefaults = (options, compilerIndex) => {
511511
/** @type {NonNullable<WebpackOptionsNormalized["loader"]>} */ (
512512
options.loader
513513
),
514-
{ targetProperties, environment: options.output.environment }
514+
{
515+
targetProperties,
516+
environment: options.output.environment,
517+
outputModule:
518+
/** @type {NonNullable<WebpackOptionsNormalized["output"]["module"]>} */
519+
(options.output.module)
520+
}
515521
);
516522

517523
F(options, "externalsType", () => {
@@ -2251,9 +2257,13 @@ const applyExternalsPresetsDefaults = (
22512257
* @param {object} options options
22522258
* @param {TargetProperties | false} options.targetProperties target properties
22532259
* @param {Environment} options.environment environment
2260+
* @param {boolean} options.outputModule is output type is module
22542261
* @returns {void}
22552262
*/
2256-
const applyLoaderDefaults = (loader, { targetProperties, environment }) => {
2263+
const applyLoaderDefaults = (
2264+
loader,
2265+
{ targetProperties, environment, outputModule }
2266+
) => {
22572267
F(loader, "target", () => {
22582268
if (targetProperties) {
22592269
if (targetProperties.electron) {
@@ -2267,6 +2277,15 @@ const applyLoaderDefaults = (loader, { targetProperties, environment }) => {
22672277
if (targetProperties.bun) return "bun";
22682278
if (targetProperties.node) return "node";
22692279
if (targetProperties.web) return "web";
2280+
// no single platform to report: the bundle runs on both (target
2281+
// `"universal"` / `["web", "node"]`), so loaders get `"universal"`
2282+
if (
2283+
outputModule &&
2284+
targetProperties.node === null &&
2285+
targetProperties.web === null
2286+
) {
2287+
return "universal";
2288+
}
22702289
}
22712290
});
22722291
D(loader, "environment", environment);

test/Defaults.unittest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,7 +3054,7 @@ describe("snapshots", () => {
30543054
+ "module": true,
30553055
@@ ... @@
30563056
- "target": "web",
3057-
+ "target": undefined,
3057+
+ "target": "universal",
30583058
@@ ... @@
30593059
- "importMeta": true,
30603060
+ "importMeta": "preserve-unknown",
@@ -3240,7 +3240,7 @@ describe("snapshots", () => {
32403240
+ "module": true,
32413241
@@ ... @@
32423242
- "target": "web",
3243-
+ "target": undefined,
3243+
+ "target": "universal",
32443244
@@ ... @@
32453245
- "importMeta": true,
32463246
+ "importMeta": "preserve-unknown",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.auto-box {
2+
color: purple;
3+
}

test/configCases/target/universal-all-module-types/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import cjsValue from "./cjs";
2+
import dataUrlModule from "data:text/javascript,export default 'data-url'";
23
import jsonData from "./data.json" with { type: "json" };
34
import defer * as deferred from "./deferred.js";
45
import { esmValue } from "./esm.js";
6+
import { loaderTarget } from "./loader-target.js";
57
import autoAsset from "./small.dat";
68
import bytesRule from "./raw.bin";
79
import html from "./page.html";
810
import inlineSvg from "./inline.svg";
11+
import * as autoCssModule from "./auto.modules.css";
12+
import * as autoGlobalCss from "./plain-auto.css";
913
import * as globalCss from "./global.css";
1014
import * as cssModule from "./styles.module.css";
1115
import sheet from "./style.css" with { type: "css" };
@@ -36,6 +40,14 @@ it("supports commonjs interop", () => {
3640
expect(cjsValue).toBe("cjs");
3741
});
3842

43+
it("reports the build target to loaders", () => {
44+
expect(loaderTarget).toBe(CONFIG_NAME);
45+
});
46+
47+
it("supports `data:` uri modules", () => {
48+
expect(dataUrlModule).toBe("data-url");
49+
});
50+
3951
it("supports json modules with import attributes", () => {
4052
expect(jsonData.value).toBe(42);
4153
});
@@ -120,6 +132,16 @@ it("supports global css", () => {
120132
expect(globalCss).toEqual({});
121133
});
122134

135+
it("supports css/auto detecting css modules by filename", () => {
136+
expect(typeof autoCssModule["auto-box"]).toBe("string");
137+
expect(autoGlobalCss).toEqual({});
138+
});
139+
140+
it("supports css in an async chunk", async () => {
141+
const { lazyClass } = await import("./lazy-style.js");
142+
expect(typeof lazyClass).toBe("string");
143+
});
144+
123145
it("supports html modules", () => {
124146
expect(typeof html).toBe("string");
125147
expect(html).toContain("<img");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as style from "./lazy.module.css";
2+
3+
export const lazyClass = style["lazy-box"];
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.lazy-box {
2+
color: orange;
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
3+
// Exposes the loader context's `target` so the case can assert what loaders see.
4+
/** @type {import("../../../../").LoaderDefinitionFunction} */
5+
module.exports = function loaderTargetLoader() {
6+
return `export const loaderTarget = ${JSON.stringify(this.target)};`;
7+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const loaderTarget = "replaced by loader-target-loader";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.plain-auto-box {
2+
color: teal;
3+
}

0 commit comments

Comments
 (0)