Last 12 weeks · 332 commits
5 of 6 standards met
Describe the bug In index.html I dynamically load a static CSS file from : This works fine in dev mode and the links are ordered as defined, but in the prod build, vite renders the link tags in a different order, e.g. bundled style files seem to always append to end of `` which breaks the order (and in turn the CSS precedence): Issue https://github.com/vitejs/vite/issues/6375 sounds similar but the root cause might be a different one as in this case, CSS is not loaded via JS, but directly from HTML. Reproduction https://github.com/silverwind/vite-style-order System Info Used Package Manager npm Logs _No response_ Validations [X] Follow our Code of Conduct [X] Read the Contributing Guidelines. [X] Read the docs. [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead. [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server. [X] The provided reproduction is a minimal reproducible example of the bug.
Description The deprecation of customResolver assumes plugins are an equivalent replacement — but for CSS @import they are not, so the deprecation currently has no viable migration path. Vite 8 deprecates resolve.alias entries that use customResolver (warned from resolveResolveOptions). The suggested replacement is a plugin resolveId hook. However, CSS @import resolution never runs user plugins, so customResolver is the only mechanism able to customize how CSS imports are resolved. Following the deprecation guidance breaks CSS resolution. This affects any setup that needs programmatic/conditional resolution of CSS @import specifiers (multi-layer frameworks, monorepos, virtual layer aliases, "first existing file wins" resolution, etc.). Minimal repro: Add a resolve.alias entry with a customResolver that maps ~layers/foo.css → an existing file. It works, but logs the deprecation warning. Remove customResolver; instead register a plugin with an enforce: 'pre' resolveId hook that resolves the same specifier. In a .js/.vue file the import resolves; in a CSS @import (especially a nested one) it fails with ENOENT. Suggested solution Either: (A) CSS @import resolution consults user resolveId hooks (or a documented CSS-resolution hook), giving the deprecation a real migration path; or (B) customResolver on resolve.alias is not deprecated for the CSS-resolution case until an equivalent plugin API exists; or (C) a documented, supported API to inject a custom CSS @import resolver (e.g. css.resolve / a resolver hook honored by both the PostCSS and Lightning CSS paths). Alternative _No response_ Additional context Real-world use case: a layered Nuxt framework resolving ~layers/ across multiple filesystem roots ("first existing file wins"), which requires programmatic resolution for both JS and CSS imports. The plugin resolveId path covers JS/Vue/SSR/virtual modules perfectly; only CSS @import still forces customResolver. Validations [x] Follow our Code of Conduct [x] Read the Contributing Guidelines. [x] Read the docs. [x] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
Describe the bug import React, { useState, useEffect, useCallback, useRef } from "react"; import { LayoutDashboard, Users, FileText, Receipt, Stamp, MessageCircle, Plus, X, Search, Phone, Mail, Calendar, ChevronRight, Check, Clock, AlertCircle, Send, Paperclip, MoreHorizontal, ArrowUpRight, Trash2, Edit3, Plane } from "lucide-react"; import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Tooltip, CartesianGrid, } from "recharts"; / ------------------------------------------------------------------ / / DESIGN TOKENS / / ------------------------------------------------------------------ / const FONTS = ; const T = { ink: "#12203A", ink2: "#1C2E4E", paper: "#F8F5EE", paperCard: "#FFFFFF", paper2: "#F1EBDC", teal: "#0F726B", tealDeep: "#0B5A54", tealTint: "#E2F1EE", gold: "#D6A419", goldTint: "#FBF0D3", coral: "#D9502F", coralTint: "#FBE5DD", slate: "#5C6577", line: "#DDD5C1", lineDark: "#33456B", }; const STATUSES = ["New", "Contacted", "Quoted", "Negotiation", "Booked", "Lost"]; const STATUS_COLOR = { New: { bg: T.tealTint, fg: T.tealDeep }, Contacted: { bg: T.goldTint, fg: "#8A6408" }, Quoted: { bg: "#E7E9FB", fg: "#3B3F9B" }, Negotiation: { bg: "#FDEFD9", fg: "#8A5A08" }, Booked: { bg: "#DFF3E4", fg: "#1E7A3B" }, Lost: { bg: T.coralTint, fg: T.coral }, }; / ------------------------------------------------------------------ / / SEED DATA / / ------------------------------------------------------------------ / function seedLeads() { return [ { id: "L-1042", name: "Priya Nair", destination: "Bali, Indonesia", source: "Website", status: "Quoted", value: 3200, phone: "+91 98765 43210", email: "priya.nair@example.com", agent: "Rahul", travelers: 2, tripDate: "2026-11-04", createdAt: "2026-08-02" }, { id: "L-1043", name: "The Fernandes Family", destination: "Switzerland", source: "Referral", status: "Negotiation", value: 9800, phone: "+91 90210 11223", email: "fernandes.family@example.com", agent: "Meera", travelers: 4, tripDate: "2026-12-18", createdAt: "2026-08-05" }, { id: "L-1044", name: "Arjun Malhotra", destination: "Dubai, UAE", source: "Instagram Ad", status: "New", value: 1650, phone: "+91 88990 22110", email: "arjun.m@example.com", agent: "Rahul", travelers: 1, tripDate: "2026-09-30", createdAt: "2026-08-14" }, { id: "L-1045", name: "Sana & Imran", destination: "Maldives", source: "WhatsApp", status: "Booked", value: 5400, phone: "+91 77009 33445", email: "sana.imran@example.com", agent: "Divya", travelers: 2, tripDate: "2026-10-12", createdAt: "2026-07-22" }, { id: "L-1046", name: "Kavita Rao", destination: "Kyoto, Japan", source: "Website", status: "Contacted", value: 4100, phone: "+91 99887 66554", email: "kavita.rao@example.com", agent: "Meera", travelers: 2, tripDate: "2027-03-08", createdAt: "2026-08-16" }, { id: "L-1047", name: "Team Outbound - Zynta Ltd", destination: "Phuket, Thailand", source: "Corporate Enquiry", status: "Quoted", value: 12800, phone: "+91 96001 22334", email: "travel@zynta.example.com", agent: "Rahul", travelers: 14, tripDate: "2026-11-20", createdAt: "2026-08-10" }, { id: "L-1048", name: "Devansh Gupta", destination: "Santorini, Greece", source: "Google Ads", status: "Lost", value: 2900, phone: "+91 90112 33221", email: "devansh.g@example.com", agent: "Divya", travelers: 2, tripDate: "2026-09-05", createdAt: "2026-07-28" }, { id: "L-1049", name: "Whitmore Family", destination: "Kerala Backwaters", source: "Referral", status: "New", value: 2100, phone: "+44 7700 900123", email: "j.whitmore@example.com", agent: "Meera", travelers: 3, tripDate: "2026-12-02", createdAt: "2026-08-19" }, ]; } function seedQuotes() { return [ { id: "Q-3301", leadId: "L-1042", leadName: "Priya Nair", destination: "Bali, Indonesia", status: "Sent", total: 3200, days: [ { day: 1, title: "Arrival & Seminyak Beach", detail: "Airport pickup, check-in, sunset at Seminyak Beach." }, { day: 2, title: "Ubud Culture Trail", detail: "Tegalalang rice terrace, Monkey Forest, Ubud Palace." }, { day: 3, title: "Nusa Penida Island Hop", detail: "Speedboat to Nusa Penida, Kelingking Beach viewpoint." }, { day: 4, title: "Leisure & Departure", detail: "Spa morning, airport transfer." }, ], }, { id: "Q-3302", leadId: "L-1043", leadName: "The Fernandes Family", destination: "Switzerland", status: "Draft", total: 9800, days: [ { day: 1, title: "Zurich Arrival", detail: "Private transfer to hotel, evening old-town walk." }, { day: 2, title: "Lucerne & Mt. Titlis", detail: "Cable car to Titlis, glacier cave, Lucerne lake cruise." }, { day: 3, title: "Interlaken & Jungfraujoch", detail: "Top of Europe rail journey, Interlaken free evening." }, { day: 4, title: "Zermatt & Matterhorn", detail: "Gornergrat railway, alpine village exploration." }, { day: 5, title: "Departure", detail: "Transfer to Zurich airport." }, ], }, { id: "Q-3303", leadId: "L-1047", leadName: "Team Outbound - Zynta Ltd", destination: "Phuket, Thailand", status: "Sent", total: 12800, days: [ { day: 1, title: "Arrival & Team Welcome Dinner", detail: "Group transfer, welcome dinner at beachfront resort." }, { day: 2, title: "Island Hopping - Phi Phi", detail: "Speedboat tour, snorkeling, Maya Bay." }, { day: 3, title: "Team Building Day", detail: "Facilitated outdoor activities and workshop." }, { day: 4, title: "Free Day & Departure", detail: "Leisure time, airport transfers staggered by flight." }, ], }, { id: "Q-3304", leadId: "L-1045", leadName: "Sana & Imran", destination: "Maldives", status: "Accepted", total: 5400, days: [ { day: 1, title: "Overwater Villa Check-in", detail: "Seaplane transfer, welcome drinks, sunset cruise." }, { day: 2, title: "Snorkeling & Reef Tour", detail: "Guided snorkeling excursion, private beach dinner." }, { day: 3, title: "Spa & Relaxation", detail: "Couples spa session, free day at resort." }, { day: 4, title: "Departure", detail: "Seaplane transfer to Male airport." }, ], }, ]; } function seedInvoices() { return [ { id: "INV-8801", leadName: "Sana & Imran", quoteId: "Q-3304", amount: 5400, status: "Paid", dueDate: "2026-08-10", issuedDate: "2026-07-30" }, { id: "INV-8802", leadName: "Team Outbound - Zynta Ltd", quoteId: "Q-3303", amount: 6400, status: "Pending", dueDate: "2026-08-30", issuedDate: "2026-08-12" }, { id: "INV-8803", leadName: "Priya Nair", quoteId: "Q-3301", amount: 1600, status: "Overdue", dueDate: "2026-08-18", issuedDate: "2026-08-04" }, { id: "INV-8804", leadName: "The Fernandes Family", quoteId: "Q-3302", amount: 3000, status: "Pending", dueDate: "2026-09-01", issuedDate: "2026-08-15" }, ]; } function seedVisas() { return [ { id: "V-501", leadName: "The Fernandes Family", country: "Switzerland (Schengen)", status: "In Process", submitted: "2026-08-06", expected: "2026-08-27" }, { id: "V-502", leadName: "Kavita Rao", country: "Japan", status: "Not Started", submitted: "-", expected: "-" }, { id: "V-503", leadName: "Sana & Imran", country: "Maldives (Visa on arrival)", status: "Approved", submitted: "2026-07-25", expected: "2026-07-25" }, { id: "V-504", leadName: "Whitmore Family", country: "India e-Visa", status: "Submitted", submitted: "2026-08-19", expected: "2026-08-24" }, ]; } function seedMessages() { return [ { id: "W-1", leadName: "Priya Nair", phone: "+91 98765 43210", last: "Perfect, please share the Nusa Penida add-on cost too.", time: "10:42 AM", unread: 2 }, { id: "W-2", leadName: "The Fernandes Family", phone: "+91 90210 11223", last: "We'd like to add 1 extra night in Zermatt.", time: "9:15 AM", unread: 0 }, { id: "W-3", leadName: "Arjun Malhotra", phone: "+91 88990 22110", last: "Is the Burj Khalifa ticket included?", time: "Yesterday", unread: 1 }, { id: "W-4", leadName: "Sana & Imran", phone: "+91 77009 33445", last: "Thank you! Everything was perfect ❤️", time: "Mon", unread: 0 }, { id: "W-5", leadName: "Team Outbound - Zynta Ltd", phone: "+91 96001 22334", last: "Sharing the final headcount by Friday.", time: "Mon", unread: 0 }, ]; } / ------------------------------------------------------------------ / / STORAGE HELPERS / / ------------------------------------------------------------------ / async function loadTable(key, seeder) { try { const res = await window.storage.get(key, false); if (res && res.value) return JSON.parse(res.value); throw new Error("empty"); } catch (e) { const seeded = seeder(); try { await window.storage.set(key, JSON.stringify(seeded), false); } catch (_) {} return seeded; } } async function saveTable(key, data) { try { await window.storage.set(key, JSON.stringify(data), false); } catch (_) {} } / ------------------------------------------------------------------ / / SPLIT-FLAP DIGIT DISPLAY (signature element) / / ------------------------------------------------------------------ / function SplitFlap({ text, size = 22 }) { const chars = String(text).split(""); return ( {chars.map((c, i) => ( {c} ))} {} ); } / ------------------------------------------------------------------ / / SMALL UI PRIMITIVES / / ------------------------------------------------------------------ / function Pill({ children, bg, fg }) { return ( {children} ); } function IconButton({ icon: Icon, onClick, title, danger }) { return ( ); } function PrimaryButton({ children, onClick, icon: Icon }) { return ( {Icon && } {children} ); } / Ticket-stub card shell used for leads / quotes — the boarding-pass motif / function TicketStub({ children, accent = T.teal, rightWidth = 92, right }) { return ( {children} {right} ); } / ------------------------------------------------------------------ / / MODAL SHELL / / ------------------------------------------------------------------ / function Modal({ title, onClose, children, width = 480 }) { return ( e.stopPropagation()} style={{ background: T.paperCard, borderRadius: 14, width, maxWidth: "100%", maxHeight: "88vh", overflowY: "auto", boxShadow: "0 20px 60px rgba(0,0,0,0.3)", }}> {title} {children} ); } function Field({ label, children }) { return ( {label} {children} ); } const inputStyle = { width: "100%", padding: "9px 11px", borderRadius: 8, border: , fontFamily: "'Inter', sans-serif", fontSize: 14, color: T.ink, boxSizing: "border-box", outline: "none", }; / ------------------------------------------------------------------ / / SIDEBAR / / ------------------------------------------------------------------ / const NAV = [ { key: "dashboard", label: "Control Tower", icon: LayoutDashboard }, { key: "leads", label: "Leads", icon: Users }, { key: "quotes", label: "Itineraries & Quotes", icon: FileText }, { key: "invoices", label: "Invoicing", icon: Receipt }, { key: "visas", label: "Visa Tracker", icon: Stamp }, { key: "whatsapp", label: "WhatsApp Desk", icon: MessageCircle }, ]; function Sidebar({ active, setActive, counts }) { return ( Voyageboard Travel Ops CRM {NAV.map((item) => { const isActive = active === item.key; const Icon = item.icon; const count = counts[item.key]; return ( setActive(item.key)} style={{ display: "flex", alignItems: "center", gap: 11, width: "100%", padding: "10px 12px", borderRadius: 9, border: "none", marginBottom: 3, background: isActive ? T.ink2 : "transparent", color: isActive ? "#fff" : "#B7C3DA", cursor: "pointer", fontSize: 13.5, fontWeight: isActive ? 600 : 500, textAlign: "left", borderLeft: isActive ? : "3px solid transparent", }} {item.label} {count != null && ( {count} )} ); })} RA Rahul Advani Senior Travel Agent ); } / ------------------------------------------------------------------ / / DASHBOARD / / ------------------------------------------------------------------ / function Dashboard({ leads, quotes, invoices, visas }) { const pipelineData = STATUSES.filter(s => s !== "Lost").map(s => ({ status: s, count: leads.filter(l => l.status === s).length, })); const pendingInvoiceTotal = invoices.filter(i => i.status !== "Paid").reduce((a, b) => a + b.amount, 0); const activeQuoteValue = quotes.filter(q => q.status !== "Accepted").reduce((a, b) => a + b.total, 0); const visasInProcess = visas.filter(v => v.status === "In Process" v.status === "Submitted").length; const recent = [...leads].sort((a, b) => (a.createdAt Today · Aug 22, 2026 Control Tower {[ { label: "Total Leads", value: String(leads.length), sub: }, { label: "Open Quote Value", value: , sub: }, { label: "Outstanding Invoices", value: , sub: }, { label: "Visas In Process", value: String(visasInProcess), sub: }, ].map((kpi) => ( {kpi.label} {kpi.sub} ))} Pipeline by Stage Recent Leads {recent.map((l) => ( {l.name.slice(0, 2).toUpperCase()} {l.name} {l.destination} {l.status} ))} ); } / ------------------------------------------------------------------ / / LEADS VIEW / / ------------------------------------------------------------------ / function AddLeadModal({ onClose, onSave }) { const [form, setForm] = useState({ name: "", destination: "", source: "Website", value: "", phone: "", email: "", agent: "Rahul", travelers: 1, tripDate: "", }); const set = (k) => (e) => setForm({ ...form, [k]: e.target.value }); const submit = () => { if (!form.name !form.destination) return; onSave({ id: "L-" + Math.floor(1000 + Math.random() 9000), status: "New", createdAt: new Date().toISOString().slice(0, 10), ...form, value: Number(form.value) 1, }); onClose(); }; return ( {["Website", "Referral", "Instagram Ad", "Google Ads", "WhatsApp", "Corporate Enquiry"].map(s => {s})} Add Lead ); } function LeadsView({ leads, setLeads }) { const [query, setQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("All"); const [showAdd, setShowAdd] = useState(false); const [selected, setSelected] = useState(null); const filtered = leads.filter(l => (statusFilter === "All" l.status === statusFilter) && (l.name.toLowerCase().includes(query.toLowerCase()) l.destination.toLowerCase().includes(query.toLowerCase())) ); const updateStatus = (id, status) => { const next = leads.map(l => l.id === id ? { ...l, status } : l); setLeads(next); if (selected?.id === id) setSelected({ ...selected, status }); }; const removeLead = (id) => { setLeads(leads.filter(l => l.id !== id)); setSelected(null); }; return ( Leads {filtered.length} of {leads.length} leads setShowAdd(true)} icon={Plus}>New Lead setQuery(e.target.value)} /> setStatusFilter(e.target.value)}> All {STATUSES.map(s => {s})} {filtered.map((l) => ( Value ${l.value.toLocaleString()} } {l.name} {l.status} {l.destination} · {l.travelers} traveler{l.travelers > 1 ? "s" : ""} · {l.source} {l.id} · trip {l.tripDate "TBD"} updateStatus(l.id, e.target.value)} style={{ ...inputStyle, padding: "5px 7px", fontSize: 12, width: 128 }} {STATUSES.map(s => {s})} setSelected(l)} /> removeLead(l.id)} /> ))} {filtered.length === 0 && ( No leads match your search — try a different filter. )} {showAdd && setShowAdd(false)} onSave={(l) => setLeads([l, ...leads])} />} {selected && ( setSelected(null)}> {selected.phone} {selected.email} Trip date: {selected.tripDate "TBD"} Assigned agent: {selected.agent} )} ); } / ------------------------------------------------------------------ / / QUOTES / ITINERARY VIEW / / ------------------------------------------------------------------ / const QUOTE_STATUS_COLOR = { Draft: { bg: "#EEE", fg: T.slate }, Sent: { bg: T.goldTint, fg: "#8A6408" }, Accepted: { bg: "#DFF3E4", fg: "#1E7A3B" }, }; function QuotesView({ quotes, leads }) { const [open, setOpen] = useState(null); return ( Itineraries & Quotes AI-assisted day-by-day itineraries linked to your leads {quotes.map((q) => ( setOpen(q)}> {q.destination} {q.id} {q.status} {q.leadName} {q.days.length}-day itinerary ${q.total.toLocaleString()} View ))} {open && ( setOpen(null)} width={560}> {open.status} for {open.leadName} {open.days.map((d) => ( {d.day} {d.title} {d.detail} ))} Total quoted ${open.total.toLocaleString()} )} ); } / ------------------------------------------------------------------ / / INVOICES VIEW / / ------------------------------------------------------------------ / const INVOICE_COLOR = { Paid: { bg: "#DFF3E4", fg: "#1E7A3B" }, Pending: { bg: T.goldTint, fg: "#8A6408" }, Overdue: { bg: T.coralTint, fg: T.coral }, }; function InvoicesView({ invoices, setInvoices }) { const markPaid = (id) => setInvoices(invoices.map(i => i.id === id ? { ...i, status: "Paid" } : i)); const totals = { paid: invoices.filter(i => i.status === "Paid").reduce((a, b) => a + b.amount, 0), pending: invoices.filter(i => i.status === "Pending").reduce((a, b) => a + b.amount, 0), overdue: invoices.filter(i => i.status === "Overdue").reduce((a, b) => a + b.amount, 0), }; return ( Invoicing Track payments across every booking {[ { label: "Collected", value: totals.paid, color: "#1E7A3B" }, { label: "Pending", value: totals.pending, color: "#8A6408" }, { label: "Overdue", value: totals.overdue, color: T.coral }, ].map(c => ( {c.label} ${c.value.toLocaleString()} ))} InvoiceClientAmountDueStatus {invoices.map((i) => ( {i.id} {i.leadName} ${i.amount.toLocaleString()} {i.dueDate} {i.status} {i.status !== "Paid" && ( markPaid(i.id)} style={{ fontSize: 11.5, fontWeight: 600, color: T.teal, background: T.tealTint, border: "none", padding: "5px 9px", borderRadius: 7, cursor: "pointer", }}>Mark paid )} ))} ); } / ------------------------------------------------------------------ / / VISA TRACKER / / ------------------------------------------------------------------ / const VISA_STEPS = ["Not Started", "Submitted", "In Process", "Approved"]; const VISA_COLOR = { "Not Started": T.slate, "Submitted": "#8A6408", "In Process": T.tealDeep, "Approved": "#1E7A3B", "Rejected": T.coral, }; function VisaTracker({ visas, setVisas }) { const advance = (id) => { setVisas(visas.map(v => { if (v.id !== id) return v; const idx = VISA_STEPS.indexOf(v.status); if (idx === -1 idx === VISA_STEPS.length - 1) return v; return { ...v, status: VISA_STEPS[idx + 1] }; })); }; return ( Visa Tracker Real-time visa application progress by traveler {visas.map((v) => { const isRejected = v.status === "Rejected"; const stepIndex = VISA_STEPS.indexOf(v.status); return ( {v.leadName} {v.country} · {v.id} {v.status} {!isRejected && ( {VISA_STEPS.map((s, idx) => ( {idx : idx + 1} {idx )} ))} )} Submitted {v.submitted} · Expected {v.expected} {!isRejected && stepIndex advance(v.id)} style={{ fontSize: 11.5, fontWeight: 600, color: T.tealDeep, background: T.tealTint, border: "none", padding: "5px 10px", borderRadius: 7, cursor: "pointer", }}>Advance stage )} ); })} ); } / ------------------------------------------------------------------ / / WHATSAPP DESK / / ------------------------------------------------------------------ / function WhatsAppView({ threads, setThreads }) { const [activeId, setActiveId] = useState(threads[0]?.id); const [chatLog, setChatLog] = useState({}); const [draft, setDraft] = useState(""); const active = threads.find(t => t.id === activeId) threads[0]; useEffect(() => { if (!threads.some(t => t.id === activeId) && threads[0]) setActiveId(threads[0].id); }, [threads]); const seedLog = (t) => ([ { from: "them", text: t.last, time: t.time }, ]); const log = chatLog[active?.id] (active ? seedLog(active) : []); const send = () => { if (!draft.trim() !active) return; const newLog = [...log, { from: "me", text: draft, time: "Now" }]; setChatLog({ ...chatLog, [active.id]: newLog }); setThreads(threads.map(t => t.id === active.id ? { ...t, last: draft, time: "Now", unread: 0 } : t)); setDraft(""); }; if (!active) return No conversations yet.; return ( WhatsApp Desk Every lead conversation in one inbox {threads.map((t) => ( setActiveId(t.id)} style={{ padding: "12px 14px", cursor: "pointer", background: t.id === active.id ? T.tealTint : "transparent", borderBottom: , }}> {t.leadName} {t.time} {t.last} {t.unread > 0 && ( {t.unread} )} ))} {active.leadName} {active.phone} {log.map((m, idx) => ( {m.text} {m.time} ))} setDraft(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} placeholder="Type a message..." style={{ ...inputStyle, flex: 1 }} /> ); } / ------------------------------------------------------------------ / / ROOT APP / / ------------------------------------------------------------------ */ export default function VoyageboardCRM() { const [active, setActive] = useState("dashboard"); const [loaded, setLoaded] = useState(false); const [leads, setLeadsState] = useState([]); const [quotes, setQuotesState] = useState([]); const [invoices, setInvoicesState] = useState([]); const [visas, setVisasState] = useState([]); const [messages, setMessagesState] = useState([]); useEffect(() => { (async () => { const [l, q, i, v, m] = await Promise.all([ loadTable("vb-leads", seedLeads), loadTable("vb-quotes", seedQuotes), loadTable("vb-invoices", seedInvoices), loadTable("vb-visas", seedVisas), loadTable("vb-messages", seedMessages), ]); setLeadsState(l); setQuotesState(q); setInvoicesState(i); setVisasState(v); setMessagesState(m); setLoaded(true); })(); }, []); const setLeads = useCallback((next) => { setLeadsState(next); saveTable("vb-leads", next); }, []); const setInvoices = useCallback((next) => { setInvoicesState(next); saveTable("vb-invoices", next); }, []); const setVisas = useCallback((next) => { setVisasState(next); saveTable("vb-visas", next); }, []); const setMessages = useCallback((next) => { setMessagesState(next); saveTable("vb-messages", next); }, []); const counts = { leads: leads.length, quotes: quotes.length, invoices: invoices.filter(i => i.status !== "Paid").length, visas: visas.filter(v => v.status !== "Approved" && v.status !== "Rejected").length, whatsapp: messages.reduce((a, m) => a + (m.unread 0), 0), }; if (!loaded) { return ( {FONTS} Loading Voyageboard… ); } return ( {FONTS} {active === "dashboard" && } {active === "leads" && } {active === "quotes" && } {active === "invoices" && } {active === "visas" && } {active === "whatsapp" && } ); } Reproduction Voyageboard crm Steps to reproduce _No response_ System Info Used Package Manager yarn Logs _No response_ Validations [x] Follow our Code of Conduct [x] Read the Contributing Guidelines. [x] Read the docs. [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. [x] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead. [x] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server. [x] The provided reproduction is a minimal reproducible example of the bug.
Describe the bug strips from before cac parses it. This is necessary because cac does not know the flag. However, it also removes the next non-flag token, as if the flag took a value: has never taken a value. The profiler is started unconditionally and no code ever reads a value for it, since the flag was introduced. So the second splice only eats a real positional argument whenever the flag is written before it: 1. swallows and silently starts the dev server in the current directory. 2. builds the current directory instead of . 3. swallows the subcommand itself and starts a dev server instead of building. Writing the flag after the positional () is unaffected, which is why this went unnoticed. Note that every other flag supports the flag-first order, for example builds correctly. Reproduction Self-contained, see the steps below. Steps to reproduce The second command tries to build the current directory and fails: Expected: it behaves like with profiling enabled. The dev-server variant () is worse. It produces no error at all and just serves the wrong directory. System Info Used Package Manager pnpm Logs _No response_ Validations [x] Follow our Code of Conduct [x] Read the Contributing Guidelines. [x] Read the docs. [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. [x] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead. [x] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server. [x] The provided reproduction is a minimal reproducible example of the bug.
Repository: vitejs/vite. Description: Next generation frontend tooling. It's fast! Stars: 82702, Forks: 8705. Primary language: TypeScript. Languages: TypeScript (83.2%), JavaScript (9.3%), HTML (4.4%), CSS (2.5%), Vue (0.2%). License: MIT. Homepage: http://vite.dev Topics: build-tool, dev-server, frontend, hmr, vite. Latest release: create-vite@9.2.0 (1w ago). Open PRs: 100, open issues: 658. Last activity: 9h ago. Community health: 87%. Top contributors: yyx990803, sapphi-red, patak-cat, bluwy, renovate[bot], antfu, btea, hi-ogawa, underfin, shulaoda and others.