Skip to content

Commit bf97859

Browse files
Merge branch 'main' into refactor/dedicated-module-classes
2 parents ece40bd + 30b18a6 commit bf97859

90 files changed

Lines changed: 1646 additions & 696 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": minor
3+
---
4+
5+
Skip import specifiers, `require()` and `import()` calls in dead conditional branches gated by inlined imported constants (`isDEV ? A : B`), evaluated via `getCondition`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": minor
3+
---
4+
5+
Add CSS parser `as` option and resolve `url()` inside HTML `style` attributes.

.github/workflows/test.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ jobs:
161161
- run: yarn cover:unit --ci --cacheDirectory .jest-cache
162162

163163
- name: Codecov
164-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
164+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
165165
with:
166166
flags: unit
167167
env:
@@ -212,7 +212,7 @@ jobs:
212212
SHARD: ${{ matrix.shard }}
213213

214214
- name: Codecov
215-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
215+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
216216
with:
217217
flags: test262
218218
env:
@@ -250,7 +250,7 @@ jobs:
250250
- run: yarn cover:html5lib --ci --cacheDirectory .jest-cache
251251

252252
- name: Codecov
253-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
253+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
254254
with:
255255
flags: html5lib
256256
env:
@@ -288,7 +288,7 @@ jobs:
288288
- run: yarn cover:css-parsing --ci --cacheDirectory .jest-cache
289289

290290
- name: Codecov
291-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
291+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
292292
with:
293293
flags: css-parsing
294294
env:
@@ -407,7 +407,7 @@ jobs:
407407
if: matrix.node-version != '10.x' && matrix.node-version != '12.x' && matrix.node-version != '14.x' && matrix.node-version != '16.x' && matrix.node-version != '18.x' && matrix.node-version != '20.x'
408408

409409
- name: Codecov
410-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
410+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
411411
with:
412412
files: ./coverage/coverage-nyc.json,./coverage/coverage-final.json
413413
directory: ./coverage/

cspell.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@
378378
"sccs",
379379
"FEFF",
380380
"apos",
381-
"Abaz"
381+
"Abaz",
382+
"Guardable"
382383
],
383384
"ignoreRegExpList": [
384385
"/Author.+/",

declarations/WebpackOptions.d.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,10 @@ export type AssetParserDataUrlFunction =
803803
* Enable/disable renaming of `@keyframes`.
804804
*/
805805
export type CssParserAnimation = boolean;
806+
/**
807+
* Configure how the CSS source is parsed: as a full stylesheet (default) or as a block's contents (e.g. the content of an HTML `style` attribute).
808+
*/
809+
export type CssParserAs = "stylesheet" | "block-contents";
806810
/**
807811
* Enable/disable renaming of `@container` names.
808812
*/
@@ -3240,6 +3244,10 @@ export interface CssAutoOrModuleParserOptions {
32403244
* Enable/disable renaming of `@keyframes`.
32413245
*/
32423246
animation?: CssParserAnimation;
3247+
/**
3248+
* Configure how the CSS source is parsed: as a full stylesheet (default) or as a block's contents (e.g. the content of an HTML `style` attribute).
3249+
*/
3250+
as?: CssParserAs;
32433251
/**
32443252
* Enable/disable renaming of `@container` names.
32453253
*/
@@ -3343,6 +3351,10 @@ export interface CssModuleParserOptions {
33433351
* Enable/disable renaming of `@keyframes`.
33443352
*/
33453353
animation?: CssParserAnimation;
3354+
/**
3355+
* Configure how the CSS source is parsed: as a full stylesheet (default) or as a block's contents (e.g. the content of an HTML `style` attribute).
3356+
*/
3357+
as?: CssParserAs;
33463358
/**
33473359
* Enable/disable renaming of `@container` names.
33483360
*/
@@ -3384,6 +3396,10 @@ export interface CssModuleParserOptions {
33843396
* Parser options for css modules.
33853397
*/
33863398
export interface CssParserOptions {
3399+
/**
3400+
* Configure how the CSS source is parsed: as a full stylesheet (default) or as a block's contents (e.g. the content of an HTML `style` attribute).
3401+
*/
3402+
as?: CssParserAs;
33873403
/**
33883404
* Configure how CSS content is exported as default.
33893405
*/
@@ -3574,15 +3590,16 @@ export interface HtmlParserOptions {
35743590
*/
35753591
tag?: string;
35763592
/**
3577-
* How the attribute value should be parsed and bundled. `src` extracts a single URL as a plain asset; `srcset` parses a `srcset`-style list of candidate URLs as plain assets; `script` and `script-module` emit a classic / ES-module chunk entry like `<script src>` and `<script type="module" src>`; `stylesheet` emits a CSS chunk entry like `<link rel="stylesheet">`; `stylesheet-inline` treats the attribute value as inline CSS text and bundles it through the CSS pipeline (the attribute's content is replaced with the processed CSS at render time, like an inline `<style>` body).
3593+
* How the attribute value should be parsed and bundled. `src` extracts a single URL as a plain asset; `srcset` parses a `srcset`-style list of candidate URLs as plain assets; `script` and `script-module` emit a classic / ES-module chunk entry like `<script src>` and `<script type="module" src>`; `stylesheet` emits a CSS chunk entry like `<link rel="stylesheet">`; `stylesheet-style` treats the attribute value as a full stylesheet (like a `<style>` body) and `stylesheet-style-attribute` as a CSS block's contents (a declaration list, like a `style` attribute) — both bundle it through the CSS pipeline and replace the attribute's content with the processed CSS at render time.
35783594
*/
35793595
type:
35803596
| "src"
35813597
| "srcset"
35823598
| "script"
35833599
| "script-module"
35843600
| "stylesheet"
3585-
| "stylesheet-inline";
3601+
| "stylesheet-style"
3602+
| "stylesheet-style-attribute";
35863603
}
35873604
)[]
35883605
| boolean;

lib/ExportsInfo.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,7 +1609,7 @@ class ExportInfo {
16091609
* Move the target forward as long resolveTargetFilter is fulfilled
16101610
* @param {ModuleGraph} moduleGraph the module graph
16111611
* @param {ResolveTargetFilter} resolveTargetFilter filter function to further resolve target
1612-
* @param {(target: TargetItemWithConnection) => ModuleGraphConnection=} updateOriginalConnection updates the original connection instead of using the target connection
1612+
* @param {((target: TargetItemWithConnection) => ModuleGraphConnection | undefined)=} updateOriginalConnection updates the original connection instead of using the target connection
16131613
* @returns {TargetItemWithConnection | undefined} the resolved target when moved
16141614
*/
16151615
moveTarget(moduleGraph, resolveTargetFilter, updateOriginalConnection) {
@@ -1633,7 +1633,7 @@ class ExportInfo {
16331633
/** @type {Target} */
16341634
(this._target).set(undefined, {
16351635
connection: updateOriginalConnection
1636-
? updateOriginalConnection(target)
1636+
? updateOriginalConnection(target) || target.connection
16371637
: target.connection,
16381638
export: /** @type {NonNullable<TargetItemWithConnection["export"]>} */ (
16391639
target.export

lib/config/defaults.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,16 @@ const applyModuleDefaults = (
13001300
},
13011301
resolve
13021302
});
1303+
// A `style="..."` attribute is a CSS block's contents, not a
1304+
// stylesheet, so parse it as one (`as: "block-contents"`).
1305+
rules.push({
1306+
dependency: "html-style-attribute",
1307+
parser: {
1308+
exportType: "text",
1309+
as: "block-contents"
1310+
},
1311+
resolve
1312+
});
13031313
}
13041314
}
13051315

lib/css/CssGenerator.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -908,14 +908,15 @@ class CssGenerator extends Generator {
908908
continue;
909909
}
910910

911-
// Inline `<style>` blocks in HTML modules read the merged CSS
912-
// text directly via the `css-text` source type — they don't go
913-
// through the JS-string wrapper that other consumers use.
914-
// Matched by dependency category so the CSS package doesn't
915-
// have to import HtmlInlineStyleDependency.
911+
// Inline `<style>` blocks and `style="..."` attributes in HTML
912+
// modules read the merged CSS text directly via the `css-text`
913+
// source type — they don't go through the JS-string wrapper that
914+
// other consumers use. Matched by dependency category so the CSS
915+
// package doesn't have to import HtmlInlineStyleDependency.
916916
if (
917917
connection.dependency &&
918-
connection.dependency.category === "html-style"
918+
(connection.dependency.category === "html-style" ||
919+
connection.dependency.category === "html-style-attribute")
919920
) {
920921
hasCssText = true;
921922
continue;

lib/css/CssParser.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ class CssParser extends Parser {
745745
this.defaultMode =
746746
typeof options.defaultMode !== "undefined" ? options.defaultMode : "pure";
747747
this.options = {
748+
as: "stylesheet",
748749
url: true,
749750
import: true,
750751
namedExports: true,
@@ -3408,9 +3409,15 @@ class CssParser extends Parser {
34083409
}
34093410
}
34103411
};
3412+
// `as` selects the top-level production (§5.3): a `style` attribute is a
3413+
// block's contents, everything else a full stylesheet (the default).
34113414
new SourceProcessor()
34123415
.use(/** @type {VisitorMap} */ (visitors))
3413-
.process(source, { locConverter, comment });
3416+
.process(source, {
3417+
locConverter,
3418+
comment,
3419+
as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as)
3420+
});
34143421

34153422
/** @type {BuildInfo} */
34163423
(module.buildInfo).strict = true;

lib/css/walkCssTokens.js

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,10 +2101,15 @@ const declarationStartLikely = (ts) => {
21012101

21022102
/**
21032103
* Consume a block's contents, CSS Syntax Level 3 [§5.4.5](https://drafts.csswg.org/css-syntax/#consume-block-contents). Per tabatkins/parse-css.js reference impl: returns separate `decls` and `rules` flat lists, both preserved on EOF / `}` (the spec text's "Return rules" single-list model drops trailing decls because there's no implicit flush before EOF / `}`).
2104+
*
2105+
* `onNode` is the same streaming extension `consumeAStylesheetsContents` exposes:
2106+
* when given, each consumed declaration / rule is handed to it immediately (in
2107+
* source order) instead of being collected, so the returned lists are empty.
21042108
* @param {TokenStream} ts token stream
2105-
* @returns {{ decls: Declaration[], rules: Rule[] }} consumed decls + rules (stops at the enclosing `}` / EOF, left in the stream)
2109+
* @param {((node: Declaration | Rule) => void)=} onNode optional per-node sink (streaming); nodes are not collected when given
2110+
* @returns {{ decls: Declaration[], rules: Rule[] }} consumed decls + rules (both empty when `onNode` is given; stops at the enclosing `}` / EOF, left in the stream)
21062111
*/
2107-
const consumeABlocksContents = (ts) => {
2112+
const consumeABlocksContents = (ts, onNode) => {
21082113
/** @type {Declaration[]} */
21092114
const decls = [];
21102115
/** @type {Rule[]} */
@@ -2128,7 +2133,10 @@ const consumeABlocksContents = (ts) => {
21282133
// Consume an at-rule from input, with nested set to true. If a rule was returned, append it to rules.
21292134
else if (t.type === TT_AT_KEYWORD) {
21302135
const atRule = consumeAnAtRule(ts, true);
2131-
if (atRule) rules.push(atRule);
2136+
if (atRule) {
2137+
if (onNode) onNode(atRule);
2138+
else rules.push(atRule);
2139+
}
21322140
}
21332141
// anything else
21342142
// Mark input. Consume a declaration from input, with nested set to true.
@@ -2140,14 +2148,18 @@ const consumeABlocksContents = (ts) => {
21402148
ts.mark();
21412149
const decl = consumeADeclaration(ts, true);
21422150
if (decl) {
2143-
decls.push(decl);
2151+
if (onNode) onNode(decl);
2152+
else decls.push(decl);
21442153
ts.discardMark();
21452154
continue;
21462155
}
21472156
ts.restoreMark();
21482157
}
21492158
const rule = consumeAQualifiedRule(ts, TT_SEMICOLON, true);
2150-
if (rule) rules.push(rule);
2159+
if (rule) {
2160+
if (onNode) onNode(rule);
2161+
else rules.push(rule);
2162+
}
21512163
}
21522164
}
21532165
};
@@ -2599,12 +2611,31 @@ const unescapeIdentifier = makeCacheable(_unescapeIdentifier);
25992611
* @typedef {CompiledVisitorBucket[]} CompiledVisitorMap a sparse array indexed by node type
26002612
*/
26012613

2614+
/**
2615+
* A CSS Syntax §5.4 top-level consumer that streams each top-level node it
2616+
* produces to `onNode` (in source order) rather than collecting it. Every entry
2617+
* in `TOP_LEVEL_CONSUMERS` shares this shape, so the walk's `grammar` drives any
2618+
* `as` mode through one call — a future mode is just another map entry.
2619+
* @typedef {(ts: TokenStream, onNode: (node: Rule | Declaration) => void) => void} TopLevelConsumer
2620+
*/
2621+
2622+
/**
2623+
* `as` value → the §5.4 consumer that streams its top-level nodes. Keyed by the
2624+
* public `CssParserOptions.as` enum.
2625+
* @type {Record<string, TopLevelConsumer>}
2626+
*/
2627+
const TOP_LEVEL_CONSUMERS = {
2628+
stylesheet: /** @type {TopLevelConsumer} */ (consumeAStylesheetsContents),
2629+
"block-contents": consumeABlocksContents
2630+
};
2631+
26022632
/**
26032633
* @typedef {object} GrammarContext
26042634
* @property {CompiledVisitorMap} visitors compiled visitor map
26052635
* @property {LocConverter} locConverter shared loc converter
26062636
* @property {((input: string, start: number, end: number) => number)=} comment comment-token callback
26072637
* @property {boolean=} recurseBlocks walk into block bodies' nested rules (default true)
2638+
* @property {("stylesheet" | "block-contents")=} as which top-level production to consume the source as (see `TOP_LEVEL_CONSUMERS`): `"stylesheet"` (default) or `"block-contents"` (a block's contents, e.g. an HTML `style` attribute)
26082639
*/
26092640

26102641
/**
@@ -2709,13 +2740,13 @@ const grammar = (input, ctx) => {
27092740
}
27102741
};
27112742

2712-
// Consume a stylesheet's contents (§5.4.1) and walk each top-level rule the
2713-
// moment it's parsed (via the `onRule` sink) rather than collecting them
2714-
// first — so the whole stylesheet AST is never held at once; peak heap is
2715-
// ~one top-level rule's subtree, since each walked rule is unreferenced
2716-
// before the next is parsed.
2743+
// Stream each top-level node (selected by `as`) to the walker the moment it's
2744+
// consumed, rather than collecting them first — so the whole AST is never
2745+
// held at once; peak heap is ~one top-level node's subtree.
27172746
const ts = new TokenStream(input, 0, locConverter, comment);
2718-
consumeAStylesheetsContents(ts, (rule) => walkRule(rule, null));
2747+
const consume =
2748+
TOP_LEVEL_CONSUMERS[ctx.as || "stylesheet"] || consumeAStylesheetsContents;
2749+
consume(ts, (node) => walkRule(node, null));
27192750
};
27202751

27212752
/**
@@ -2764,15 +2795,16 @@ class SourceProcessor {
27642795
* Run the grammar over `input`, firing visitors in source order. No
27652796
* AST retained.
27662797
* @param {string} input source text
2767-
* @param {{ locConverter?: LocConverter, comment?: (input: string, start: number, end: number) => number, recurseBlocks?: boolean }=} ctx reuse a `locConverter`, forward a `comment` callback, or set `recurseBlocks: false` to stop at top-level rules
2798+
* @param {{ locConverter?: LocConverter, comment?: (input: string, start: number, end: number) => number, recurseBlocks?: boolean, as?: ("stylesheet" | "block-contents") }=} ctx reuse a `locConverter`, forward a `comment` callback, set `recurseBlocks: false` to stop at top-level rules, or set `as: "block-contents"` to parse a block's contents (e.g. an HTML `style` attribute) instead of a full stylesheet
27682799
*/
27692800
process(input, ctx = {}) {
27702801
const locConverter = ctx.locConverter || new LocConverter(input);
27712802
grammar(input, {
27722803
visitors: this._visitors,
27732804
locConverter,
27742805
comment: ctx.comment,
2775-
recurseBlocks: ctx.recurseBlocks
2806+
recurseBlocks: ctx.recurseBlocks,
2807+
as: ctx.as
27762808
});
27772809
}
27782810
}

0 commit comments

Comments
 (0)