Skip to content

fix(utils): keep the url when a density descriptor omits its leading zero - #23346

Open
NgoQuocViet2001 wants to merge 2 commits into
vitejs:mainfrom
NgoQuocViet2001:fix-srcset-fractional-density
Open

fix(utils): keep the url when a density descriptor omits its leading zero#23346
NgoQuocViet2001 wants to merge 2 commits into
vitejs:mainfrom
NgoQuocViet2001:fix-srcset-fractional-density

Conversation

@NgoQuocViet2001

Copy link
Copy Markdown
Contributor

Description

A density descriptor in srcset / image-set() may be written without its leading zero — .5x is as valid as 0.5x. When one is, parseSrcset() discards the image URL and returns the descriptor in its place.

parseSrcset('./nested/asset.png .5x, ./nested/asset.png 1x')
// [ { url: '.5x',                descriptor: ''   },   <- url lost
//   { url: './nested/asset.png', descriptor: '1x' } ]

Because the URL never reaches the candidate list, base-URL rewriting and asset resolution run on .5x, and the first candidate ends up pointing at nothing:

processSrcSetSync('./nested/asset.png .5x, ./nested/asset.png 1x', prependBase)
  before  '/base/.5x, /base/nested/asset.png 1x'
  after   '/base/nested/asset.png .5x, /base/nested/asset.png 1x'

Quoted URLs are mangled the same way, and worse — the quote handling desynchronises:

'"./nested/asset.png" .75x,"./nested/asset.png" 1x'
  before  '"/base/75x,"./nested/asset.png" 1x'
  after   '"/base/nested/asset.png" .75x, "/base/nested/asset.png" 1x'

Why

The descriptor group starts with \w:

(?:\s(?<descriptor>\w[^,]+))?

A dot is not \w, so the optional group fails to match. The url alternation [^,]\S*[^,] then happily consumes .5x as the url, and the actual path is left out of the match.

The fix allows a dot as the descriptor's first character. [^,]+ still requires two or more characters, which every legal descriptor satisfies (1x, .5x, 400w, 600dpi) — that constraint is pre-existing and unchanged.

Validation

Both new tests fail on main and pass with the change:

$ npx vitest run .../utils.spec.ts -t "leading zero"     # utils.ts reverted
 FAIL  ... > keep the url when a density descriptor omits its leading zero
   expected '/base/.5x, /base/nested/asset.png 1x' to be '/base/nested/asset.png .5x, …'
 FAIL  ... > keep the quoted url when a density descriptor omits its leading zero
   expected '"/base/75x,"./nested/asset.png" 1x' to be '"/base/nested/asset.png" .75x, …'
  Tests  2 failed | 122 skipped (124)
  • npx vitest run packages/vite/src/node/__tests__/utils.spec.ts124 passed (124).
  • I also ran every distinct source string from the existing processSrcSetSync block through the old and the new pattern — base64, comma-in-URL, no-descriptor, url()-with-commas, 400w, 600dpi, quoted URLs, 1.5x — and the parsed output is identical in all of them. The only inputs whose behaviour changes are the ones that were broken.
  • npx eslint and npx oxfmt --check on both files — clean.

Same code path as the image-set() handling in plugins/css.ts, so that gets the fix too.

…zero

A density descriptor may be written without its leading zero -- `.5x`
is as valid as `0.5x`. The descriptor group started with `\w`, which a
dot does not match, so the group failed and the url alternation matched
the descriptor instead:

    './nested/asset.png .5x, ./nested/asset.png 1x'
    -> [{ url: '.5x', descriptor: '' }, { url: './nested/asset.png', descriptor: '1x' }]

The real url is dropped from the candidate list entirely, so base-URL
rewriting and asset resolution operate on '.5x' and the first candidate
points at nothing.

Allow a dot as the descriptor's first character.
@github-actions github-actions Bot added the bot: maybe Maybe a bot, LLM, or agent label Aug 24, 2026
@NgoQuocViet2001

Copy link
Copy Markdown
Contributor Author

Build&Test: node-24.15.0, windows-latest failed here (1 of 20; every other job including ubuntu and macOS on the same Node version is green). I went looking for a cause in this change and could not find one — posting what I checked rather than just asking for a re-run.

The regex change is a no-op on every real fixture in the repo. I extracted all 14 distinct srcset= and image-set(…) values from playground/assets, playground/css and playground/html, and ran both the old and the new pattern over them:

14 fixture values, 0 differ

That includes the base64 candidates, the url()-with-commas cases, type('image/png') 1x, the linear-gradient(...) 2x case and the multiple-descriptor case. None of them has a descriptor starting with ., which is the only input the widened character class can affect.

Unit tests are unaffected. On this branch vs unmodified main, same Windows machine:

main        Tests  26 failed | 940 passed | 3 skipped (969)
this branch Tests  26 failed | 942 passed | 3 skipped (971)

Same 26 failures either way — all in packages/create-vite/__tests__/cli.spec.ts, which is environmental on my box and unrelated to this change. The delta is exactly the 2 tests this PR adds, both passing.

Same base, different result. #23345 branches from the same commit and passed that identical Windows job, 20/20.

So I have no evidence this change caused it, and some evidence it did not — but the job log is not readable without an authenticated token, so I can't name the failing test to be sure. If someone can re-run the Windows job, that would settle it; if it reproduces, please paste the failing test name and I'll dig in properly rather than guess.

Comment thread packages/vite/src/node/utils.ts Outdated
Comment on lines +816 to +819
// The descriptor may start with a dot: a density descriptor is allowed to omit
// its leading zero (`.5x`). Without the dot in the leading class the descriptor
// group fails, the alternation then matches the descriptor itself as the url,
// and the real image url is dropped from the candidate list.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// The descriptor may start with a dot: a density descriptor is allowed to omit
// its leading zero (`.5x`). Without the dot in the leading class the descriptor
// group fails, the alternation then matches the descriptor itself as the url,
// and the real image url is dropped from the candidate list.

Don't need to explain a bug

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed in 17fca76 — dropped the whole four-line block, not just the suggested line, since the rest of it was explaining the same bug. The existing comment above the regex is untouched.

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

Labels

bot: maybe Maybe a bot, LLM, or agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants