/// import { defineConfig } from 'vitest/config'; import vueJsxVapor from 'vue-jsx-vapor/vite'; import tailwindcss from '@tailwindcss/vite'; import { syncEnginePlugin } from 'vue-sync-engine/plugin'; import { VitePWA } from 'vite-plugin-pwa'; // JSX компилируется сразу в Vapor-код (без interop и virtual DOM). // Tailwind v4 подключён как Vite-плагин, конфиг живёт в src/app.css (@theme). // syncEnginePlugin собирает дефы в virtual:sync-engine-registry — его требует // DevTools-ветка движка в dev-режиме. export default defineConfig({ plugins: [ vueJsxVapor(), tailwindcss(), syncEnginePlugin({ definitions: ['/src/data/defs.ts'] }), // Установка на домашний экран — единственный способ вывести IndexedDB // из-под 7-дневной очистки ITP в WebKit (Safari и Chrome на iOS). VitePWA({ registerType: 'autoUpdate', injectRegister: 'auto', // Иконки и -теги берутся из pwa-assets.config.ts. pwaAssets: { config: true }, manifest: { name: 'Ккал — дневник питания', short_name: 'Ккал', description: 'Локальный дневник питания: калории, БЖУ, вес. Данные хранятся только на устройстве.', lang: 'ru', display: 'standalone', orientation: 'portrait', start_url: '/', scope: '/', theme_color: '#12100d', background_color: '#12100d', }, workbox: { // woff тут дубли woff2 от fontsource — в прекеш идёт только woff2. globPatterns: ['**/*.{js,css,html,woff2,svg,png,ico}'], runtimeCaching: [ // Распознавание штрихкодов на WebKit — ~1 МБ wasm. В прекеш его класть // жалко (нужен не всем), поэтому кэшируем после первого запуска // сканера: дальше он работает и офлайн. Файл с хэшем в имени, так что // CacheFirst безопасен. { urlPattern: /\.wasm$/, handler: 'CacheFirst', options: { cacheName: 'wasm', expiration: { maxEntries: 4 }, cacheableResponse: { statuses: [0, 200] }, }, }, // Штрихкоды из Open Food Facts: сеть свежее, но офлайн отдаём кэш. { urlPattern: /^https:\/\/world\.openfoodfacts\.org\/.*/, handler: 'NetworkFirst', options: { cacheName: 'off-api', expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 }, cacheableResponse: { statuses: [0, 200] }, }, }, ], }, }), ], resolve: { // vue-sync-engine подключён симлинком (link:), поэтому его импорты `vue` // уходят в собственную vue из devDependencies движка. Без dedupe в бандл // попадают две копии Vue и mount падает на чужом appContext. dedupe: ['vue'], }, optimizeDeps: { // Движок ходит в virtual-модуль — пребандл прятал бы его от плагина. exclude: ['vue-sync-engine'], }, define: { __VUE_OPTIONS_API__: 'false', __VUE_PROD_DEVTOOLS__: 'false', __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false', // Dev-ветки vue-sync-engine (DevTools-панель) остаются в `vite dev`, // вырезаются из прод-сборки. __SYNC_ENGINE_DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'), }, test: { environment: 'node', include: ['src/**/*.test.ts'], }, });