Skip to content

feat(html,css): support DefinePlugin text substitution in HTML and CS… - #21474

Open
aryanraj45 wants to merge 3 commits into
webpack:mainfrom
aryanraj45:feat/define-plugin-html-css
Open

feat(html,css): support DefinePlugin text substitution in HTML and CS…#21474
aryanraj45 wants to merge 3 commits into
webpack:mainfrom
aryanraj45:feat/define-plugin-html-css

Conversation

@aryanraj45

@aryanraj45 aryanraj45 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

DefinePlugin only tapped JS parsers, so HTML and CSS modules had no build-time identifier substitution. This adds an optional { type: "html" | "css" } second argument to DefinePlugin, enabling %ENV_VAR%-style replacements in HTML and CSS source — matching what Vite does natively with %VITE_APP_TITLE%.

  • "javascript" (default) — existing behaviour, unchanged
  • "html" — replaces any defined identifier in HTML module source before parsing
  • "css" — replaces any defined identifier in CSS module source before parsing (all four CSS module types covered)

Adds a hooks.program SyncBailHook to HtmlParser and CssParser, following the same pattern already established on JavascriptParser.

What kind of change does this PR introduce?

feat

Did you add tests for your changes?

Yes — test/configCases/html/define-plugin/ (HTML substitution) and test/configCases/css/define-plugin/ (CSS substitution).

Does this PR introduce a breaking change?

No. Omitting the second argument keeps the existing JS-only behaviour exactly as before.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

DefinePlugin docs should note the new optional { type } option with an HTML/CSS example.

Use of AI

Yes


Note

Medium Risk
Touches core DefinePlugin compilation wiring and pre-parse source rewriting for HTML/CSS; incorrect substitution or cache keys could affect builds, but default JS behavior is preserved and behavior is covered by new config cases.

Overview
DefinePlugin now accepts an optional second argument { type?: "javascript" | "html" | "css" } (default javascript). Existing JS behavior is unchanged when the option is omitted.

For type: "html" or "css", definitions are applied by substituting literal keys in the module source on NormalModule's processResult hook (before HTML/CSS parsers run), so patterns like %BASE_URL%favicon.ico resolve correctly and DefinePlugin.runtimeValue still works via a minimal parser shim. Replacements sort by longest key first to handle overlapping keys (e.g. process.env vs process.env.NODE_ENV), and valueDependencies (including a main hash) tie into the same cache invalidation model as JS defines.

html/css instances no longer merge into the shared JS definitions hook, so HTML-only keys cannot leak into JavaScript object expansion when a separate JS DefinePlugin shares a prefix (e.g. CFG).

Type definitions and config-case tests cover basic HTML/CSS substitution, URL/base-path cases, overlap, JS isolation, and CSS property replacement.

Reviewed by Cursor Bugbot for commit 7d01c44. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI review requested due to automatic review settings July 22, 2026 06:27
@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7d01c44

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@aryanraj45
aryanraj45 force-pushed the feat/define-plugin-html-css branch from dfb057e to 15b32de Compare July 22, 2026 06:47
Copilot AI review requested due to automatic review settings July 22, 2026 06:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@aryanraj45
aryanraj45 force-pushed the feat/define-plugin-html-css branch from 15b32de to 7f5073f Compare July 22, 2026 06:55
@aryanraj45

Copy link
Copy Markdown
Contributor Author

@alexander-akait can you please have a look at this thanks :))

@aryanraj45
aryanraj45 force-pushed the feat/define-plugin-html-css branch from 7f5073f to 4abd787 Compare August 7, 2026 08:24
Comment thread lib/DefinePlugin.js
Comment thread lib/DefinePlugin.js Outdated
Comment thread lib/DefinePlugin.js
Comment thread lib/DefinePlugin.js Outdated
…runs

Replacements were recorded from a parse hook, so the parser still saw the
original text: a URL built from a define resolved verbatim, and its own
dependency ranges overlapped the ConstDependency ones. Rewriting the source
from processResult fixes both and drops the parser hooks added for it.

Also carries the module into runtimeValue (neither text parser has a
`state`), records the main definitions hash so adding or removing a define
invalidates a cached module, and keeps html/css defines out of the merged
tree JavaScript object expansion reads.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7d01c44. Configure here.

Comment thread lib/DefinePlugin.js
out += text.slice(last, from) + replacementCode;
last = to;
}
return [out + text.slice(last), ...rest];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewritten source keeps stale loader source map

Medium Severity

The substituted text is returned together with the untouched loader source map from result, so mappings no longer line up with the emitted content. CSS/HTML modules produced by preprocessor loaders (for example sass-loader) with devtool enabled get silently misaligned source maps, and values containing newlines shift whole lines. TypeScriptPlugin composes an updated map when it rewrites source in the same hook.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7d01c44. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants