fix: apply lint fixes and fix vite manualChunks for Vite 8 compatibility

This commit is contained in:
Kirill
2026-05-15 13:43:51 +05:00
parent d8798de49a
commit ed475be289
7 changed files with 575 additions and 43 deletions
+1 -1
View File
@@ -24,8 +24,8 @@ import { ToggleCartIcon } from '@/features/cart/toggle-cart-icon'
import { formatPriceRub } from '@/shared/lib/format-price'
import { reviewsCountRu } from '@/shared/lib/reviews-count-ru'
import { $user } from '@/shared/model/auth'
import { RichTextMessageContent } from '@/shared/ui/RichTextMessageContent'
import { OptimizedImage } from '@/shared/ui/OptimizedImage'
import { RichTextMessageContent } from '@/shared/ui/RichTextMessageContent'
export function ProductPage() {
const user = useUnit($user)
+14 -31
View File
@@ -15,7 +15,7 @@ type OptimizedImageProps = {
/** Extract UUID and subdir from a /uploads/... URL */
function parseUploadUrl(src: string): { uuid: string; ext: string; subdir: string } | null {
const match = src.match(/^\/uploads(?:\/(reviews))?\/([^.\/]+)\.(png|jpe?g|webp)/i)
const match = src.match(/^\/uploads(?:\/(reviews))?\/([^.\\/]+)\.(png|jpe?g|webp)/i)
if (!match) return null
return { subdir: match[1] || '', uuid: match[2], ext: match[3].toLowerCase() }
}
@@ -25,9 +25,7 @@ function buildSrcSet(src: string, widths: number[]): string | null {
if (!parsed) return null
const pathPrefix = parsed.subdir ? `${parsed.subdir}/` : ''
return widths
.map((w) => `/uploads-resized/${pathPrefix}${parsed.uuid}.avif?w=${w} ${w}w`)
.join(', ')
return widths.map((w) => `/uploads-resized/${pathPrefix}${parsed.uuid}.avif?w=${w} ${w}w`).join(', ')
}
function buildFallbackSrc(src: string, width: number): string {
@@ -37,43 +35,28 @@ function buildFallbackSrc(src: string, width: number): string {
return `/uploads-resized/${pathPrefix}${parsed.uuid}.webp?w=${width}`
}
export function OptimizedImage({ src, alt, widths = DEFAULT_WIDTHS, sizes, sx, priority = false }: OptimizedImageProps) {
export function OptimizedImage({
src,
alt,
widths = DEFAULT_WIDTHS,
sizes,
sx,
priority = false,
}: OptimizedImageProps) {
const srcSet = useMemo(() => buildSrcSet(src, widths), [src, widths])
const fallbackSrc = useMemo(() => buildFallbackSrc(src, widths[0]), [src, widths])
// If src is not an upload URL, render a plain img
if (!srcSet) {
return (
<Box
component="img"
src={src}
alt={alt}
loading={priority ? 'eager' : 'lazy'}
decoding="async"
sx={sx}
/>
)
return <Box component="img" src={src} alt={alt} loading={priority ? 'eager' : 'lazy'} decoding="async" sx={sx} />
}
const sizesAttr = sizes ?? '(max-width: 600px) 320px, (max-width: 1024px) 640px, 1024px'
return (
<Box
component="picture"
sx={{ display: 'block', lineHeight: 0, ...sx }}
>
<Box
component="source"
type="image/avif"
srcSet={srcSet}
sizes={sizesAttr}
/>
<Box
component="source"
type="image/webp"
srcSet={fallbackSrc}
sizes={sizesAttr}
/>
<Box component="picture" sx={{ display: 'block', lineHeight: 0, ...sx }}>
<Box component="source" type="image/avif" srcSet={srcSet} sizes={sizesAttr} />
<Box component="source" type="image/webp" srcSet={fallbackSrc} sizes={sizesAttr} />
<Box
component="img"
src={fallbackSrc}
@@ -26,7 +26,10 @@ describe('OptimizedImage', () => {
it('handles review subdir correctly', () => {
render(<OptimizedImage src="/uploads/reviews/def456.jpg" alt="review" />)
const avifSource = screen.getByAltText('review').closest('picture')?.querySelector('source[type="image/avif"]') as HTMLSourceElement
const avifSource = screen
.getByAltText('review')
.closest('picture')
?.querySelector('source[type="image/avif"]') as HTMLSourceElement
const srcSet = avifSource?.getAttribute('srcset') ?? ''
expect(srcSet).toContain('/uploads-resized/reviews/def456.avif?w=320')
})
@@ -39,7 +42,10 @@ describe('OptimizedImage', () => {
it('respects custom widths', () => {
render(<OptimizedImage src="/uploads/abc123.png" alt="test" widths={[200, 400]} />)
const avifSource = screen.getByAltText('test').closest('picture')?.querySelector('source[type="image/avif"]') as HTMLSourceElement
const avifSource = screen
.getByAltText('test')
.closest('picture')
?.querySelector('source[type="image/avif"]') as HTMLSourceElement
const srcSet = avifSource?.getAttribute('srcset') ?? ''
expect(srcSet).toContain('?w=200')
expect(srcSet).toContain('?w=400')
@@ -10,8 +10,8 @@ import Typography from '@mui/material/Typography'
import { useQuery } from '@tanstack/react-query'
import { Link as RouterLink } from 'react-router-dom'
import { fetchLatestApprovedReviews } from '@/entities/product/api/reviews-api'
import { RichTextMessageContent } from '@/shared/ui/RichTextMessageContent'
import { OptimizedImage } from '@/shared/ui/OptimizedImage'
import { RichTextMessageContent } from '@/shared/ui/RichTextMessageContent'
function initials(display: string) {
const s = display.trim()
+11 -6
View File
@@ -34,12 +34,17 @@ export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-mui': ['@mui/material', '@mui/icons-material', '@emotion/react', '@emotion/styled'],
'vendor-swiper': ['swiper/react', 'swiper/modules'],
'vendor-query': ['@tanstack/react-query'],
'vendor-effector': ['effector', 'effector-react'],
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('react-router')) return 'vendor-react'
if (id.includes('react-dom')) return 'vendor-react'
if (id.includes('node_modules/react/')) return 'vendor-react'
if (id.includes('@mui')) return 'vendor-mui'
if (id.includes('@emotion')) return 'vendor-mui'
if (id.includes('swiper')) return 'vendor-swiper'
if (id.includes('@tanstack/react-query')) return 'vendor-query'
if (id.includes('effector')) return 'vendor-effector'
}
},
},
},