Repository: vercel/next.js
File: packages/next/src/shared/lib/get-img-props.ts (TypeScript)

Code before:
  src: string
  srcSet: string | undefined
  sizes: string | undefined
}

function generateImgAttrs({
  config,
  src,
  unoptimized,
  width,
  quality,
  sizes,
  loader,
}: GenImgAttrsData): GenImgAttrsResult {
  if (unoptimized) {
    if (src.startsWith('/') && !src.startsWith('//')) {
      let deploymentId = getDeploymentId()
      const srcUrl = new URL(src, 'http://n')
      const srcDpl = srcUrl.searchParams.get('dpl')
      if (srcDpl) {
        deploymentId = srcDpl
        // Remove the dpl parameter from the src URL to avoid duplication
        srcUrl.searchParams.delete('dpl')
        src = srcUrl.href.slice('http://n'.length)
      }

      if (deploymentId) {
        src += `${src.includes('?') ? '&' : '?'}dpl=${deploymentId}`
      }
    }
    return { src, srcSet: undefined, sizes: undefined }
  }

  const { widths, kind } = getWidths(config, width, sizes)
  const last = widths.length - 1

  return {
    sizes: !sizes && kind === 'w' ? '100vw' : sizes,
    srcSet: widths
      .map(
        (w, i) =>
          `${loader({ config, src, quality, width: w })} ${
            kind === 'w' ? w : i + 1
          }${kind}`
      )
      .join(', '),

    // It's intended to keep `src` the last attribute because React updates
    // attributes in order. If we keep `src` the first one, Safari will
    // immediately start to fetch `src`, before `sizes` and `srcSet` are even
    // updated by React. That causes multiple unnecessary requests if `srcSet`

Review comment (suggestion):
If there is already a dpl query param, it should keep it instead of removing it and adding the `getDeploymentId()` value.

Code after:
  src: string
  srcSet: string | undefined
  sizes: string | undefined
}

function generateImgAttrs({
  config,
  src,
  unoptimized,
  width,
  quality,
  sizes,
  loader,
}: GenImgAttrsData): GenImgAttrsResult {
  if (unoptimized) {
    if (src.startsWith('/') && !src.startsWith('//')) {
      let deploymentId = getDeploymentId()
      if (deploymentId) {
        const srcUrl = new URL(src, 'http://n')
        const srcDpl = srcUrl.searchParams.get('dpl')
        if (!srcDpl) {
          // src is missing the dpl parameter, but we have a deploymentId, so add it to the src URL
          srcUrl.searchParams.set('dpl', deploymentId)
          src = srcUrl.href.slice('http://n'.length)
        }
      }
    }
    return { src, srcSet: undefined, sizes: undefined }
  }

  const { widths, kind } = getWidths(config, width, sizes)
  const last = widths.length - 1

  return {
    sizes: !sizes && kind === 'w' ? '100vw' : sizes,
    srcSet: widths
      .map(
        (w, i) =>
          `${loader({ config, src, quality, width: w })} ${
            kind === 'w' ? w : i + 1
          }${kind}`
      )
      .join(', '),

    // It's intended to keep `src` the last attribute because React updates
    // attributes in order. If we keep `src` the first one, Safari will
    // immediately start to fetch `src`, before `sizes` and `srcSet` are even
    // updated by React. That causes multiple unnecessary requests if `srcSet`
    // and `sizes` are defined.
    // This bug cannot be reproduced in Chrome or Firefox.
    src: loader({ config, src, quality, width: widths[last] }),