diff --git a/app.go b/app.go index 3aa0108..98ae68c 100644 --- a/app.go +++ b/app.go @@ -20,7 +20,6 @@ import ( "github.com/go-vgo/robotgo" "github.com/wailsapp/wails/v2/pkg/runtime" - "golang.design/x/clipboard" ) // App struct @@ -105,24 +104,22 @@ func (a *App) StartRecording() { timestamp := time.Now().Format("20060102_150405") a.recordingPath = filepath.Join(tempDir, fmt.Sprintf("wis_recording_%s.wav", timestamp)) - // Start recording (now faster due to device reuse) - go func() { - err := a.audioRecorder.Start(a.recordingPath) - if err != nil { - logger.Error("Failed to start recording: %v", err) - // IDIOT-PROOFING: Fallback to default - if a.config.MicrophoneDevice != nil { - a.audioRecorder.SetDevice("") - err = a.audioRecorder.Start(a.recordingPath) - } - if err != nil { - if a.overlay != nil { - a.overlay.Hide() - } - return - } + // Start recording + err := a.audioRecorder.Start(a.recordingPath) + if err != nil { + logger.Error("Failed to start recording: %v", err) + // IDIOT-PROOFING: Fallback to default + if a.config.MicrophoneDevice != nil { + a.audioRecorder.SetDevice("") + err = a.audioRecorder.Start(a.recordingPath) } - }() + if err != nil { + if a.overlay != nil { + a.overlay.Hide() + } + return + } + } // 3. Handle secondary tasks in background go func() { @@ -241,7 +238,7 @@ func (a *App) processRecording() { config.Save(a.config, "") // Copy to clipboard - clipboard.Write(clipboard.FmtText, []byte(refinedText)) + runtime.ClipboardSetText(a.ctx, refinedText) // Paste a.pasteText() @@ -260,13 +257,11 @@ func (a *App) processRecording() { // pasteText simulates Ctrl+V to paste from clipboard func (a *App) pasteText() { - // Give a small delay for clipboard to update - time.Sleep(100 * time.Millisecond) + // Give a substantial delay for Linux GTK clipboard sync + time.Sleep(200 * time.Millisecond) - // Simulate Ctrl+V - robotgo.KeyToggle("control", "down") - robotgo.KeyTap("v") - robotgo.KeyToggle("control", "up") + // Simulate Ctrl+V using modern robotgo API + robotgo.KeyTap("v", "ctrl") } // GetSettings returns the current configuration @@ -291,8 +286,8 @@ func (a *App) SaveSettings(settings map[string]interface{}) string { } if val, ok := settings["shortcut"].(string); ok { // Validate shortcut before applying - trigger, _ := hotkey.ParseShortcut(val) - if len(trigger) == 0 { + _, _, ok := hotkey.ParseShortcut(val) + if !ok { logger.Error("Invalid shortcut: %s (rejected)", val) return "Invalid shortcut - must have at least one modifier and a regular key" } @@ -403,12 +398,6 @@ func (a *App) startupHeadless() { fmt.Printf("Failed to initialize logger: %v\n", err) } - // Initialize clipboard - err = clipboard.Init() - if err != nil { - logger.Error("Failed to initialize clipboard: %v", err) - } - // Load configuration configPath, err := config.GetConfigPath() if err != nil { @@ -422,43 +411,44 @@ func (a *App) startupHeadless() { } } - // Initialize heavy components in background for instant startup - go func() { - // Initialize transcriber - a.transcriber = transcriber.NewClient( - a.config.APIKey, - a.config.WhisperModel, - a.config.AIModel, - a.config.AIPrompt, - ) + // Initialize heavy components synchronously instead of in background. + // This prevents a known ALSA/GTK initialization race condition bug on Linux + // where Miniaudio and GTK try to probe audio devices concurrently resulting in SIGABRT. - // Initialize whisper manager - a.whisperManager, _ = whisper.NewManager() + // Initialize transcriber + a.transcriber = transcriber.NewClient( + a.config.APIKey, + a.config.WhisperModel, + a.config.AIModel, + a.config.AIPrompt, + ) - // Initialize Audio Recorder - rec, err := recorder.NewRecorder() - if err != nil { - logger.Error("Error initializing recorder: %v", err) - } else { - a.audioRecorder = rec + // Initialize whisper manager + a.whisperManager, _ = whisper.NewManager() + + // Initialize Audio Recorder + rec, err := recorder.NewRecorder() + if err != nil { + logger.Error("Error initializing recorder: %v", err) + } else { + a.audioRecorder = rec + } + + // Initialize Overlay + a.overlay = platform.NewOverlay() + + // Connect volume feedback from recorder to overlay + if a.audioRecorder != nil && a.overlay != nil { + a.audioRecorder.OnVolume = func(level float64) { + a.overlay.SetVolume(level) } + } - // Initialize Overlay - a.overlay = platform.NewOverlay() + // Initialize Hotkey Listener + a.hotkeyListener = hotkey.NewListener(a.config.Shortcut, a.StartRecording, a.StopRecording) + a.hotkeyListener.Start() - // Connect volume feedback from recorder to overlay - if a.audioRecorder != nil && a.overlay != nil { - a.audioRecorder.OnVolume = func(level float64) { - a.overlay.SetVolume(level) - } - } - - // Initialize Hotkey Listener - a.hotkeyListener = hotkey.NewListener(a.config.Shortcut, a.StartRecording, a.StopRecording) - a.hotkeyListener.Start() - - logger.Info("Background components initialized successfully!") - }() + logger.Info("Components initialized successfully!") logger.Info("Basic app components loaded, continuing startup...") } diff --git a/build/bin/wis-free-v3.exe b/build/bin/wis-free-v3 old mode 100644 new mode 100755 similarity index 59% rename from build/bin/wis-free-v3.exe rename to build/bin/wis-free-v3 index fde9cdf..67a4faa Binary files a/build/bin/wis-free-v3.exe and b/build/bin/wis-free-v3 differ diff --git a/frontend/dist/assets/index.9197445a.css b/frontend/dist/assets/index.9197445a.css deleted file mode 100644 index f5e5c98..0000000 --- a/frontend/dist/assets/index.9197445a.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg-color: #0b0f1a;--card-bg: rgba(30, 41, 59, .7);--input-bg: rgba(51, 65, 85, .5);--text-color: #f8fafc;--text-muted: #94a3b8;--primary-color: #3b82f6;--primary-hover: #2563eb;--accent-color: #6366f1;--border-color: rgba(75, 85, 99, .4);--success-color: #10b981;--glass-border: rgba(255, 255, 255, .1)}body{background:radial-gradient(circle at top right,#1e293b,#0b0f1a);color:var(--text-color);font-family:Inter,system-ui,-apple-system,sans-serif;margin:0;padding:30px;min-height:100vh;letter-spacing:-.01em}.container{max-width:720px;margin:0 auto}h1{font-size:28px;font-weight:700;background:linear-gradient(135deg,#60a5fa,#a78bfa);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:30px}.section{background:var(--card-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:24px;border-radius:16px;margin-bottom:24px;border:1px solid var(--glass-border);box-shadow:0 10px 15px -3px #0003;transition:transform .2s ease}.section:hover{border-color:#fff3}label{display:block;color:var(--text-muted);font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px}input[type=text],input[type=password],select,textarea{width:100%;background:var(--input-bg);border:1px solid var(--border-color);color:#fff;padding:12px 14px;border-radius:10px;font-size:14px;transition:all .2s ease;box-sizing:border-box}textarea{resize:vertical;min-height:80px}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--primary-color);background:rgba(51,65,85,.8);box-shadow:0 0 0 3px #3b82f633}.form-control{max-width:480px;width:100%}.input-wrapper{position:relative;flex:1;min-width:0}.input-wrapper input{width:100%;padding-right:44px;box-sizing:border-box;display:block}.eye-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:transparent!important;border:none!important;box-shadow:none!important;cursor:pointer;padding:4px!important;font-size:16px;opacity:.5;transition:opacity .2s;line-height:1;display:flex;align-items:center;justify-content:center}.eye-btn:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.save-status{position:fixed;bottom:30px;right:30px;background:var(--success-color);color:#fff;padding:12px 24px;border-radius:12px;font-weight:600;box-shadow:0 10px 15px -3px #0003;transform:translateY(100px);opacity:0;transition:all .3s cubic-bezier(.4,0,.2,1);z-index:1000}.save-status.show{transform:translateY(0);opacity:1}button{background-color:var(--primary-color);color:#fff;border:none;padding:10px 20px;border-radius:10px;cursor:pointer;font-weight:600;font-size:14px;transition:all .2s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0003;white-space:nowrap;flex-shrink:0}button:hover{transform:translateY(-1px);box-shadow:0 10px 15px -3px #0000004d;background-color:var(--primary-hover)}button:active{transform:translateY(0)}button[style*="background: transparent"]{background:transparent!important;box-shadow:none!important;text-decoration:underline;opacity:.7}button[style*="background: transparent"]:hover{opacity:1}.flex-row{display:flex;gap:12px;align-items:center;flex-wrap:nowrap}.flex-between{display:flex;justify-content:space-between;align-items:center}.history-list{max-height:250px;overflow-y:auto;margin-top:10px;padding-right:5px}.history-item{background:rgba(255,255,255,.03);padding:14px;border-radius:12px;margin-bottom:10px;border:1px solid rgba(255,255,255,.05);transition:all .2s ease}.history-item:hover{background:rgba(255,255,255,.06);transform:translate(2px)}.history-time{color:var(--primary-color);font-size:11px;font-weight:700;margin-bottom:6px;display:block}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)} diff --git a/frontend/dist/assets/index.33b5a8c3.js b/frontend/dist/assets/index.c6252eaf.js similarity index 68% rename from frontend/dist/assets/index.33b5a8c3.js rename to frontend/dist/assets/index.c6252eaf.js index a832afc..1892917 100644 --- a/frontend/dist/assets/index.33b5a8c3.js +++ b/frontend/dist/assets/index.c6252eaf.js @@ -1,3 +1,3 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))l(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&l(r)}).observe(document,{childList:!0,subtree:!0});function o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerpolicy&&(i.referrerPolicy=n.referrerpolicy),n.crossorigin==="use-credentials"?i.credentials="include":n.crossorigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function l(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();let u=!1;window.toggleApiKey=function(){const e=document.getElementById("apiKey"),t=document.getElementById("eyeBtn");u=!u,e.type=u?"text":"password",t.textContent=u?"\u{1F648}":"\u{1F441}\uFE0F"};window.recordShortcut=function(){const e=document.getElementById("recordBtn"),t=document.getElementById("shortcutInput");e.textContent="Recording...",e.disabled=!0,t.value="";let o="";const l=s=>{s.preventDefault(),s.stopPropagation();const d=[];s.ctrlKey&&d.push("ctrl"),s.altKey&&d.push("alt"),s.shiftKey&&d.push("shift"),(s.metaKey||s.key==="Meta"||s.key==="OS")&&d.push("win");let a=s.key.toLowerCase();a==="control"&&(a="ctrl"),a==="alt"&&(a="alt"),a==="shift"&&(a="shift"),(a==="meta"||a==="os")&&(a="win"),a===" "&&(a="space");const p=new Set(d);p.add(a);const y=Array.from(p).filter(f=>f!==a);y.push(a),o=y.join("+"),t.value=o,["ctrl","alt","shift","win"].includes(a)||i()},n=s=>{s.preventDefault(),s.stopPropagation(),o&&(o.split("+").every(a=>["ctrl","alt","shift","win"].includes(a))&&o.includes("+")?setTimeout(i,100):!s.ctrlKey&&!s.altKey&&!s.shiftKey&&!s.metaKey&&i())},i=()=>{if(!o){e.textContent="Record",e.disabled=!1,r();return}window.go.main.App.SaveSettings({shortcut:o}).then(()=>{e.textContent="Record",e.disabled=!1,r()})},r=()=>{window.removeEventListener("keydown",l,!0),window.removeEventListener("keyup",n,!0)};window.addEventListener("keydown",l,!0),window.addEventListener("keyup",n,!0)};async function w(){try{const e=await window.go.main.App.GetSettings();document.getElementById("apiKey").value=e.api_key||"",document.getElementById("shortcutInput").value=e.shortcut||"alt+z",document.getElementById("whisperModel").value=e.whisper_model||"whisper-large-v3-turbo",document.getElementById("aiModel").value=e.ai_model||"llama-3.3-70b-versatile",document.getElementById("aiPrompt").value=e.ai_prompt||"",document.getElementById("language").value=e.language||"en",document.getElementById("startupToggle").checked=e.startup||!1;const t=e.history||[],o=document.getElementById("historyList");t.length>0&&(o.innerHTML="",[...t].reverse().forEach(l=>{const n=document.createElement("div");n.className="history-item";const i=document.createElement("span");i.className="history-time",i.textContent=new Date(l.timestamp).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});const r=document.createElement("div");r.textContent=l.text,n.appendChild(i),n.appendChild(r),o.appendChild(n)}))}catch(e){console.error("Failed to load settings:",e)}}async function g(){try{const e=await window.go.main.App.GetMicrophones(),t=document.getElementById("micDevice");e.forEach(o=>{if(o.index!==-1){const l=document.createElement("option");l.value=o.index,l.textContent=o.name,t.appendChild(l)}})}catch(e){console.error("Failed to load mics:",e)}}window.saveApiKey=async function(){const e=document.getElementById("apiKey").value.trim();await window.go.main.App.SaveSettings({api_key:e}),c()};function c(e="Settings Saved!"){const t=document.getElementById("saveStatus");t.textContent=e,t.classList.add("show"),setTimeout(()=>t.classList.remove("show"),2e3)}window.saveWhisperModel=async function(){await window.go.main.App.SaveSettings({whisper_model:document.getElementById("whisperModel").value}),c()};window.saveAiModel=async function(){await window.go.main.App.SaveSettings({ai_model:document.getElementById("aiModel").value}),c()};window.saveLanguage=async function(){await window.go.main.App.SaveSettings({language:document.getElementById("language").value}),c("Language Saved!")};window.saveMicDevice=async function(){const e=document.getElementById("micDevice").value;await window.go.main.App.SaveSettings({microphone_device:e==="default"?null:parseInt(e)}),c()};window.savePrompt=async function(){await window.go.main.App.SaveSettings({ai_prompt:document.getElementById("aiPrompt").value.trim()}),c("Prompt Saved!")};window.toggleStartup=async function(){await window.go.main.App.ToggleStartup(document.getElementById("startupToggle").checked)};window.clearHistory=async function(){confirm("Clear all history?")&&(await window.go.main.App.ClearHistory(),document.getElementById("historyList").innerHTML='
No history yet.
')};async function h(){try{const e=await window.go.main.App.CheckOnline(),t=document.getElementById("connectionStatus");if(e)t.innerHTML="\u{1F7E2} Online - Cloud transcription available",t.style.background="rgba(34, 197, 94, 0.1)",t.style.color="#22c55e";else{t.innerHTML="\u{1F534} Offline - Install local Whisper for transcription",t.style.background="rgba(239, 68, 68, 0.1)",t.style.color="#ef4444";const o=document.getElementById("aiModel"),l=document.getElementById("whisperModel");for(let n of o.options)n.value!=="None"&&!n.value.startsWith("local-")&&(n.disabled=!0,n.text.includes("(offline)")||(n.text+=" (offline)"))}}catch(e){console.error("Connection check failed:",e)}}async function m(){try{const e=await window.go.main.App.GetWhisperInfo(),t=document.getElementById("whisperModel"),o=t.querySelector('option[value^="local-"]');if(o&&o.remove(),e.installed){document.getElementById("whisperNotInstalled").style.display="none",document.getElementById("whisperInstalled").style.display="block",document.getElementById("installedModel").textContent=e.model+" model";const l=document.createElement("option");l.value="local-"+e.model,l.textContent="\u{1F5A5}\uFE0F Local - "+e.model+" (offline)",l.style.fontWeight="bold",t.insertBefore(l,t.firstChild)}else document.getElementById("whisperNotInstalled").style.display="block",document.getElementById("whisperInstalled").style.display="none"}catch(e){console.error("Whisper status check failed:",e)}}window.installWhisper=async function(){const e=document.getElementById("whisperModelSelect").value,t=document.getElementById("installBtn");t.disabled=!0,t.textContent="Starting installation...";try{const o=await window.go.main.App.InstallWhisper(e);alert(o+` +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))a(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&a(r)}).observe(document,{childList:!0,subtree:!0});function o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerpolicy&&(i.referrerPolicy=n.referrerpolicy),n.crossorigin==="use-credentials"?i.credentials="include":n.crossorigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function a(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();let u=!1;window.toggleApiKey=function(){const e=document.getElementById("apiKey"),t=document.getElementById("eyeBtn");u=!u,e.type=u?"text":"password",t.textContent=u?"\u{1F648}":"\u{1F441}\uFE0F"};window.recordShortcut=function(){const e=document.getElementById("recordBtn"),t=document.getElementById("shortcutInput");e.textContent="Recording...",e.disabled=!0,t.value="";let o="";const a=s=>{s.preventDefault(),s.stopPropagation();const c=[];s.ctrlKey&&c.push("ctrl"),s.altKey&&c.push("alt"),s.shiftKey&&c.push("shift"),(s.metaKey||s.key==="Meta"||s.key==="OS")&&c.push("win");let l=s.key.toLowerCase();l==="control"&&(l="ctrl"),l==="alt"&&(l="alt"),l==="shift"&&(l="shift"),(l==="meta"||l==="os")&&(l="win"),l===" "&&(l="space");const p=new Set(c);p.add(l);const y=Array.from(p).filter(f=>f!==l);y.push(l),o=y.join("+"),t.value=o,["ctrl","alt","shift","win"].includes(l)||i()},n=s=>{s.preventDefault(),s.stopPropagation(),o&&(o.split("+").every(l=>["ctrl","alt","shift","win"].includes(l))&&o.includes("+")?setTimeout(i,100):!s.ctrlKey&&!s.altKey&&!s.shiftKey&&!s.metaKey&&i())},i=()=>{if(!o){e.textContent="Record",e.disabled=!1,r();return}window.go.main.App.SaveSettings({shortcut:o}).then(()=>{e.textContent="Record",e.disabled=!1,r()})},r=()=>{window.removeEventListener("keydown",a,!0),window.removeEventListener("keyup",n,!0)};window.addEventListener("keydown",a,!0),window.addEventListener("keyup",n,!0)};async function w(){try{const e=await window.go.main.App.GetSettings();document.getElementById("apiKey").value=e.api_key||"",document.getElementById("shortcutInput").value=e.shortcut||"alt+z",document.getElementById("whisperModel").value=e.whisper_model||"whisper-large-v3-turbo",document.getElementById("aiModel").value=e.ai_model||"llama-3.3-70b-versatile",document.getElementById("aiPrompt").value=e.ai_prompt||"",document.getElementById("language").value=e.language||"en",document.getElementById("startupToggle").checked=e.startup||!1;const t=e.history||[],o=document.getElementById("historyList");t.length>0&&(o.innerHTML="",[...t].reverse().forEach(a=>{const n=document.createElement("div");n.className="history-item";const i=document.createElement("span");i.className="history-time",i.textContent=new Date(a.timestamp).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});const r=document.createElement("div");r.textContent=a.text,n.appendChild(i),n.appendChild(r),o.appendChild(n)}))}catch(e){console.error("Failed to load settings:",e)}}async function g(){try{const e=await window.go.main.App.GetMicrophones(),t=document.getElementById("micDevice");e.forEach(o=>{if(o.index!==-1){const a=document.createElement("option");a.value=o.index,a.textContent=o.name,t.appendChild(a)}})}catch(e){console.error("Failed to load mics:",e)}}window.saveApiKey=async function(){const e=document.getElementById("apiKey").value.trim();await window.go.main.App.SaveSettings({api_key:e}),d()};function d(e="Settings Saved!"){const t=document.getElementById("saveStatus");t.textContent=e,t.classList.add("show"),setTimeout(()=>t.classList.remove("show"),2e3)}window.saveWhisperModel=async function(){await window.go.main.App.SaveSettings({whisper_model:document.getElementById("whisperModel").value}),d()};window.saveAiModel=async function(){await window.go.main.App.SaveSettings({ai_model:document.getElementById("aiModel").value}),d()};window.saveLanguage=async function(){await window.go.main.App.SaveSettings({language:document.getElementById("language").value}),d("Language Saved!")};window.saveMicDevice=async function(){const e=document.getElementById("micDevice").value;await window.go.main.App.SaveSettings({microphone_device:e==="default"?null:parseInt(e)}),d()};window.savePrompt=async function(){await window.go.main.App.SaveSettings({ai_prompt:document.getElementById("aiPrompt").value.trim()}),d("Prompt Saved!")};window.toggleStartup=async function(){await window.go.main.App.ToggleStartup(document.getElementById("startupToggle").checked)};window.clearHistory=async function(){confirm("Clear all history?")&&(await window.go.main.App.ClearHistory(),document.getElementById("historyList").innerHTML='
No history yet.
')};async function h(){try{const e=await window.go.main.App.CheckOnline(),t=document.getElementById("connectionStatus");if(e)t.innerHTML="\u{1F7E2} Online - Cloud transcription available",t.style.background="rgba(34, 197, 94, 0.1)",t.style.color="#22c55e";else{t.innerHTML="\u{1F534} Offline - Install local Whisper for transcription",t.style.background="rgba(239, 68, 68, 0.1)",t.style.color="#ef4444";const o=document.getElementById("aiModel"),a=document.getElementById("whisperModel");for(let n of o.options)n.value!=="None"&&!n.value.startsWith("local-")&&(n.disabled=!0,n.text.includes("(offline)")||(n.text+=" (offline)"))}}catch(e){console.error("Connection check failed:",e)}}async function m(){try{const e=await window.go.main.App.GetWhisperInfo(),t=document.getElementById("whisperModel"),o=t.querySelector('option[value^="local-"]');if(o&&o.remove(),e.installed){document.getElementById("whisperNotInstalled").style.display="none",document.getElementById("whisperInstalled").style.display="block",document.getElementById("installedModel").textContent=e.model+" model";const a=document.createElement("option");a.value="local-"+e.model,a.textContent="\u{1F5A5}\uFE0F Local - "+e.model+" (offline)",a.style.fontWeight="bold",t.insertBefore(a,t.firstChild)}else document.getElementById("whisperNotInstalled").style.display="block",document.getElementById("whisperInstalled").style.display="none"}catch(e){console.error("Whisper status check failed:",e)}}window.installWhisper=async function(){const e=document.getElementById("whisperModelSelect").value,t=document.getElementById("installBtn");t.disabled=!0,t.textContent="Starting installation...";try{const o=await window.go.main.App.InstallWhisper(e);alert(o+` -A terminal window will open showing the installation progress. This may take several minutes depending on your internet speed.`),setTimeout(m,5e3)}catch(o){alert("Installation failed: "+o)}t.disabled=!1,t.textContent="\u{1F680} Install Offline Whisper"};window.uninstallWhisper=async function(){if(!!confirm("Are you sure you want to uninstall offline Whisper? This will delete the downloaded model."))try{const e=await window.go.main.App.UninstallWhisper();alert(e),m()}catch(e){alert("Uninstall failed: "+e)}};w();g();h();m(); +A terminal window will open showing the installation progress. This may take several minutes depending on your internet speed.`),setTimeout(m,5e3)}catch(o){alert("Installation failed: "+o)}t.disabled=!1,t.textContent="\u{1F680} Install Offline Whisper"};window.uninstallWhisper=async function(){if(!!confirm("Are you sure you want to uninstall offline Whisper? This will delete the downloaded model."))try{const e=await window.go.main.App.UninstallWhisper();alert(e),m()}catch(e){alert("Uninstall failed: "+e)}};async function v(){try{if(navigator.userAgent.toLowerCase().includes("linux")){const t=document.getElementById("offlineWhisperSection");t&&(t.style.display="none");const o=document.getElementById("startupLabel");o&&(o.innerText="Run on Linux OS Startup")}}catch(e){console.error("Failed to get environment:",e)}if(await w(),await g(),await h(),await m(),navigator.userAgent.toLowerCase().includes("linux")){const e=document.getElementById("whisperModel");e&&Array.from(e.options).forEach(t=>{t.value.startsWith("local-")&&t.remove()})}}v(); diff --git a/frontend/dist/assets/index.d8635aeb.css b/frontend/dist/assets/index.d8635aeb.css new file mode 100644 index 0000000..31c34a4 --- /dev/null +++ b/frontend/dist/assets/index.d8635aeb.css @@ -0,0 +1 @@ +:root{--bg-color: #0b0f1a;--card-bg: rgba(30, 41, 59, .7);--input-bg: rgba(51, 65, 85, .5);--text-color: #f8fafc;--text-muted: #94a3b8;--primary-color: #3b82f6;--primary-hover: #2563eb;--accent-color: #6366f1;--border-color: rgba(75, 85, 99, .4);--success-color: #10b981;--glass-border: rgba(255, 255, 255, .1);color-scheme:dark}body{background:radial-gradient(circle at top right,#1e293b,#0b0f1a);color:var(--text-color);font-family:Inter,system-ui,-apple-system,sans-serif;margin:0;padding:30px;min-height:100vh;letter-spacing:-.01em}.container{max-width:720px;margin:0 auto}h1{font-size:28px;font-weight:700;background:linear-gradient(135deg,#60a5fa,#a78bfa);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:30px}.section{background:var(--card-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:24px;border-radius:16px;margin-bottom:24px;border:1px solid var(--glass-border);box-shadow:0 10px 15px -3px #0003;transition:transform .2s ease}.section:hover{border-color:#fff3}label{display:block;color:var(--text-muted);font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px}input[type=checkbox]{accent-color:var(--primary-color);width:18px;height:18px;cursor:pointer}input[type=text],input[type=password],select,textarea{width:100%;background:var(--input-bg);border:1px solid var(--border-color);color:#fff;padding:12px 14px;border-radius:10px;font-size:14px;transition:all .2s ease;box-sizing:border-box}select{appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 14px center;background-size:16px;padding-right:40px}select option{background:var(--bg-color);color:#fff}textarea{resize:vertical;min-height:80px}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--primary-color);background:rgba(51,65,85,.8);box-shadow:0 0 0 3px #3b82f633}.form-control{max-width:480px;width:100%}.input-wrapper{position:relative;flex:1;min-width:0}.input-wrapper input{width:100%;padding-right:44px;box-sizing:border-box;display:block}.eye-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:transparent!important;border:none!important;box-shadow:none!important;cursor:pointer;padding:4px!important;font-size:16px;opacity:.5;transition:opacity .2s;line-height:1;display:flex;align-items:center;justify-content:center}.eye-btn:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.save-status{position:fixed;bottom:30px;right:30px;background:var(--success-color);color:#fff;padding:12px 24px;border-radius:12px;font-weight:600;box-shadow:0 10px 15px -3px #0003;transform:translateY(100px);opacity:0;transition:all .3s cubic-bezier(.4,0,.2,1);z-index:1000}.save-status.show{transform:translateY(0);opacity:1}button{background-color:var(--primary-color);color:#fff;border:none;padding:10px 20px;border-radius:10px;cursor:pointer;font-weight:600;font-size:14px;transition:all .2s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0003;white-space:nowrap;flex-shrink:0}button:hover{transform:translateY(-1px);box-shadow:0 10px 15px -3px #0000004d;background-color:var(--primary-hover)}button:active{transform:translateY(0)}button[style*="background: transparent"]{background:transparent!important;box-shadow:none!important;text-decoration:underline;opacity:.7}button[style*="background: transparent"]:hover{opacity:1}.flex-row{display:flex;gap:12px;align-items:center;flex-wrap:nowrap}.flex-between{display:flex;justify-content:space-between;align-items:center}.history-list{max-height:250px;overflow-y:auto;margin-top:10px;padding-right:5px}.history-item{background:rgba(255,255,255,.03);padding:14px;border-radius:12px;margin-bottom:10px;border:1px solid rgba(255,255,255,.05);transition:all .2s ease}.history-item:hover{background:rgba(255,255,255,.06);transform:translate(2px)}.history-time{color:var(--primary-color);font-size:11px;font-weight:700;margin-bottom:6px;display:block}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)} diff --git a/frontend/dist/index.html b/frontend/dist/index.html index ba41c4d..da016d0 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -6,8 +6,8 @@ wis-free-v3 Settings - - + + @@ -110,7 +110,7 @@
- Run on Windows Startup + Run on System Startup
-
diff --git a/frontend/index.html b/frontend/index.html index b26f393..f6f2666 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -108,7 +108,7 @@
- Run on Windows Startup + Run on System Startup
-
@@ -489,10 +489,44 @@ }; // Init - loadSettings(); - loadMics(); - checkConnection(); - checkWhisperStatus(); + async function initialize() { + // Check platform definitively using userAgent to guarantee it fires immediately without Wails timing issues + try { + const isLinux = navigator.userAgent.toLowerCase().includes('linux'); + if (isLinux) { + // Hide Offline Whisper section + const offlineSection = document.getElementById('offlineWhisperSection'); + if (offlineSection) { + offlineSection.style.display = 'none'; + } + + // Clean up 'Windows' reference in startup text specifically for Linux OS + const startupLabel = document.getElementById('startupLabel'); + if (startupLabel) { + startupLabel.innerText = 'Run on Linux OS Startup'; + } + } + } catch (err) { + console.error('Failed to get environment:', err); + } + + await loadSettings(); + await loadMics(); + await checkConnection(); + await checkWhisperStatus(); + + // If Linux, ensure Local Whisper option is removed from Whisper Select as a secondary guard + if (navigator.userAgent.toLowerCase().includes('linux')) { + const whisperSelect = document.getElementById('whisperModel'); + if (whisperSelect) { + Array.from(whisperSelect.options).forEach(opt => { + if (opt.value.startsWith('local-')) opt.remove(); + }); + } + } + } + + initialize(); diff --git a/frontend/node_modules/.bin/esbuild b/frontend/node_modules/.bin/esbuild deleted file mode 100644 index 63bb6d4..0000000 --- a/frontend/node_modules/.bin/esbuild +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@" -else - exec node "$basedir/../esbuild/bin/esbuild" "$@" -fi diff --git a/frontend/node_modules/.bin/esbuild b/frontend/node_modules/.bin/esbuild new file mode 120000 index 0000000..c83ac07 --- /dev/null +++ b/frontend/node_modules/.bin/esbuild @@ -0,0 +1 @@ +../esbuild/bin/esbuild \ No newline at end of file diff --git a/frontend/node_modules/.bin/esbuild.cmd b/frontend/node_modules/.bin/esbuild.cmd deleted file mode 100644 index cc920c5..0000000 --- a/frontend/node_modules/.bin/esbuild.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %* diff --git a/frontend/node_modules/.bin/esbuild.ps1 b/frontend/node_modules/.bin/esbuild.ps1 deleted file mode 100644 index 81ffbf9..0000000 --- a/frontend/node_modules/.bin/esbuild.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args - } else { - & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args - } else { - & "node$exe" "$basedir/../esbuild/bin/esbuild" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/frontend/node_modules/.bin/nanoid b/frontend/node_modules/.bin/nanoid deleted file mode 100644 index 46220bd..0000000 --- a/frontend/node_modules/.bin/nanoid +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@" -else - exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@" -fi diff --git a/frontend/node_modules/.bin/nanoid b/frontend/node_modules/.bin/nanoid new file mode 120000 index 0000000..e2be547 --- /dev/null +++ b/frontend/node_modules/.bin/nanoid @@ -0,0 +1 @@ +../nanoid/bin/nanoid.cjs \ No newline at end of file diff --git a/frontend/node_modules/.bin/nanoid.cmd b/frontend/node_modules/.bin/nanoid.cmd deleted file mode 100644 index 9c40107..0000000 --- a/frontend/node_modules/.bin/nanoid.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %* diff --git a/frontend/node_modules/.bin/nanoid.ps1 b/frontend/node_modules/.bin/nanoid.ps1 deleted file mode 100644 index d8a4d7a..0000000 --- a/frontend/node_modules/.bin/nanoid.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args - } else { - & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args - } else { - & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/frontend/node_modules/.bin/resolve b/frontend/node_modules/.bin/resolve deleted file mode 100644 index c043cba..0000000 --- a/frontend/node_modules/.bin/resolve +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../resolve/bin/resolve" "$@" -else - exec node "$basedir/../resolve/bin/resolve" "$@" -fi diff --git a/frontend/node_modules/.bin/resolve b/frontend/node_modules/.bin/resolve new file mode 120000 index 0000000..b6afda6 --- /dev/null +++ b/frontend/node_modules/.bin/resolve @@ -0,0 +1 @@ +../resolve/bin/resolve \ No newline at end of file diff --git a/frontend/node_modules/.bin/resolve.cmd b/frontend/node_modules/.bin/resolve.cmd deleted file mode 100644 index 1a017c4..0000000 --- a/frontend/node_modules/.bin/resolve.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\resolve\bin\resolve" %* diff --git a/frontend/node_modules/.bin/resolve.ps1 b/frontend/node_modules/.bin/resolve.ps1 deleted file mode 100644 index f22b2d3..0000000 --- a/frontend/node_modules/.bin/resolve.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args - } else { - & "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../resolve/bin/resolve" $args - } else { - & "node$exe" "$basedir/../resolve/bin/resolve" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/frontend/node_modules/.bin/rollup b/frontend/node_modules/.bin/rollup deleted file mode 100644 index 998fc16..0000000 --- a/frontend/node_modules/.bin/rollup +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@" -else - exec node "$basedir/../rollup/dist/bin/rollup" "$@" -fi diff --git a/frontend/node_modules/.bin/rollup b/frontend/node_modules/.bin/rollup new file mode 120000 index 0000000..5939621 --- /dev/null +++ b/frontend/node_modules/.bin/rollup @@ -0,0 +1 @@ +../rollup/dist/bin/rollup \ No newline at end of file diff --git a/frontend/node_modules/.bin/rollup.cmd b/frontend/node_modules/.bin/rollup.cmd deleted file mode 100644 index b3f110b..0000000 --- a/frontend/node_modules/.bin/rollup.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %* diff --git a/frontend/node_modules/.bin/rollup.ps1 b/frontend/node_modules/.bin/rollup.ps1 deleted file mode 100644 index 10f657d..0000000 --- a/frontend/node_modules/.bin/rollup.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args - } else { - & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args - } else { - & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/frontend/node_modules/.bin/vite b/frontend/node_modules/.bin/vite deleted file mode 100644 index 014463f..0000000 --- a/frontend/node_modules/.bin/vite +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@" -else - exec node "$basedir/../vite/bin/vite.js" "$@" -fi diff --git a/frontend/node_modules/.bin/vite b/frontend/node_modules/.bin/vite new file mode 120000 index 0000000..6d1e3be --- /dev/null +++ b/frontend/node_modules/.bin/vite @@ -0,0 +1 @@ +../vite/bin/vite.js \ No newline at end of file diff --git a/frontend/node_modules/.bin/vite.cmd b/frontend/node_modules/.bin/vite.cmd deleted file mode 100644 index f62e966..0000000 --- a/frontend/node_modules/.bin/vite.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %* diff --git a/frontend/node_modules/.bin/vite.ps1 b/frontend/node_modules/.bin/vite.ps1 deleted file mode 100644 index a7759bc..0000000 --- a/frontend/node_modules/.bin/vite.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args - } else { - & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../vite/bin/vite.js" $args - } else { - & "node$exe" "$basedir/../vite/bin/vite.js" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/frontend/node_modules/.package-lock.json b/frontend/node_modules/.package-lock.json index d7cf449..be0f320 100644 --- a/frontend/node_modules/.package-lock.json +++ b/frontend/node_modules/.package-lock.json @@ -42,10 +42,10 @@ "esbuild-windows-arm64": "0.15.18" } }, - "node_modules/esbuild-windows-64": { + "node_modules/esbuild-linux-64": { "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", "cpu": [ "x64" ], @@ -53,7 +53,7 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">=12" diff --git a/frontend/node_modules/esbuild-linux-64/README.md b/frontend/node_modules/esbuild-linux-64/README.md new file mode 100644 index 0000000..b2f1930 --- /dev/null +++ b/frontend/node_modules/esbuild-linux-64/README.md @@ -0,0 +1,3 @@ +# esbuild + +This is the Linux 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details. diff --git a/frontend/node_modules/esbuild-windows-64/esbuild.exe b/frontend/node_modules/esbuild-linux-64/bin/esbuild old mode 100644 new mode 100755 similarity index 60% rename from frontend/node_modules/esbuild-windows-64/esbuild.exe rename to frontend/node_modules/esbuild-linux-64/bin/esbuild index d11f069..c56b194 Binary files a/frontend/node_modules/esbuild-windows-64/esbuild.exe and b/frontend/node_modules/esbuild-linux-64/bin/esbuild differ diff --git a/frontend/node_modules/esbuild-windows-64/package.json b/frontend/node_modules/esbuild-linux-64/package.json similarity index 62% rename from frontend/node_modules/esbuild-windows-64/package.json rename to frontend/node_modules/esbuild-linux-64/package.json index 3a6ec70..aaf3ccf 100644 --- a/frontend/node_modules/esbuild-windows-64/package.json +++ b/frontend/node_modules/esbuild-linux-64/package.json @@ -1,7 +1,7 @@ { - "name": "esbuild-windows-64", + "name": "esbuild-linux-64", "version": "0.15.18", - "description": "The Windows 64-bit binary for esbuild, a JavaScript bundler.", + "description": "The Linux 64-bit binary for esbuild, a JavaScript bundler.", "repository": "https://github.com/evanw/esbuild", "license": "MIT", "preferUnplugged": true, @@ -9,7 +9,7 @@ "node": ">=12" }, "os": [ - "win32" + "linux" ], "cpu": [ "x64" diff --git a/frontend/node_modules/esbuild-windows-64/README.md b/frontend/node_modules/esbuild-windows-64/README.md deleted file mode 100644 index a99ee7c..0000000 --- a/frontend/node_modules/esbuild-windows-64/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# esbuild - -This is the Windows 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details. diff --git a/frontend/node_modules/esbuild-windows-64/bin/esbuild b/frontend/node_modules/esbuild-windows-64/bin/esbuild deleted file mode 100644 index 808c4d8..0000000 --- a/frontend/node_modules/esbuild-windows-64/bin/esbuild +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node - -// Unfortunately even though npm shims "bin" commands on Windows with auto- -// generated forwarding scripts, it doesn't strip the ".exe" from the file name -// first. So it's possible to publish executables via npm on all platforms -// except Windows. I consider this a npm bug. -// -// My workaround is to add this script as another layer of indirection. It'll -// be slower because node has to boot up just to shell out to the actual exe, -// but Windows is somewhat of a second-class platform to npm so it's the best -// I can do I think. -const esbuild_exe = require.resolve('esbuild-windows-64/esbuild.exe'); -const child_process = require('child_process'); -child_process.spawnSync(esbuild_exe, process.argv.slice(2), { stdio: 'inherit' }); diff --git a/frontend/node_modules/esbuild/bin/esbuild b/frontend/node_modules/esbuild/bin/esbuild old mode 100644 new mode 100755 index e25f515..c56b194 Binary files a/frontend/node_modules/esbuild/bin/esbuild and b/frontend/node_modules/esbuild/bin/esbuild differ diff --git a/frontend/node_modules/nanoid/bin/nanoid.cjs b/frontend/node_modules/nanoid/bin/nanoid.cjs old mode 100644 new mode 100755 diff --git a/frontend/node_modules/resolve/bin/resolve b/frontend/node_modules/resolve/bin/resolve old mode 100644 new mode 100755 diff --git a/frontend/node_modules/rollup/dist/bin/rollup b/frontend/node_modules/rollup/dist/bin/rollup old mode 100644 new mode 100755 diff --git a/frontend/node_modules/vite/bin/vite.js b/frontend/node_modules/vite/bin/vite.js old mode 100644 new mode 100755 diff --git a/frontend/node_modules/vite/dist/node/chunks/dep-3e87c7b2.js b/frontend/node_modules/vite/dist/node/chunks/dep-3e87c7b2.js index 9655a6c..d418ffe 100644 --- a/frontend/node_modules/vite/dist/node/chunks/dep-3e87c7b2.js +++ b/frontend/node_modules/vite/dist/node/chunks/dep-3e87c7b2.js @@ -13820,91 +13820,91 @@ var utils$g = {}; var array$1 = {}; -Object.defineProperty(array$1, "__esModule", { value: true }); -array$1.splitWhen = array$1.flatten = void 0; -function flatten$1(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -array$1.flatten = flatten$1; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} +Object.defineProperty(array$1, "__esModule", { value: true }); +array$1.splitWhen = array$1.flatten = void 0; +function flatten$1(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +array$1.flatten = flatten$1; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} array$1.splitWhen = splitWhen; var errno$1 = {}; -Object.defineProperty(errno$1, "__esModule", { value: true }); -errno$1.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} +Object.defineProperty(errno$1, "__esModule", { value: true }); +errno$1.isEnoentCodeError = void 0; +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} errno$1.isEnoentCodeError = isEnoentCodeError; var fs$h = {}; -Object.defineProperty(fs$h, "__esModule", { value: true }); -fs$h.createDirentFromStats = void 0; -class DirentFromStats$1 { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats$1(name, stats) { - return new DirentFromStats$1(name, stats); -} +Object.defineProperty(fs$h, "__esModule", { value: true }); +fs$h.createDirentFromStats = void 0; +class DirentFromStats$1 { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats$1(name, stats) { + return new DirentFromStats$1(name, stats); +} fs$h.createDirentFromStats = createDirentFromStats$1; var path$h = {}; -Object.defineProperty(path$h, "__esModule", { value: true }); -path$h.removeLeadingDotSegment = path$h.escape = path$h.makeAbsolute = path$h.unixify = void 0; -const path$g = require$$0$4; -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -path$h.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path$g.resolve(cwd, filepath); -} -path$h.makeAbsolute = makeAbsolute; -function escape$3(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -path$h.escape = escape$3; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} +Object.defineProperty(path$h, "__esModule", { value: true }); +path$h.removeLeadingDotSegment = path$h.escape = path$h.makeAbsolute = path$h.unixify = void 0; +const path$g = require$$0$4; +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +path$h.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path$g.resolve(cwd, filepath); +} +path$h.makeAbsolute = makeAbsolute; +function escape$3(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +path$h.escape = escape$3; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } + } + return entry; +} path$h.removeLeadingDotSegment = removeLeadingDotSegment; var pattern$1 = {}; @@ -16008,173 +16008,173 @@ micromatch$1.braceExpand = (pattern, options) => { var micromatch_1 = micromatch$1; -Object.defineProperty(pattern$1, "__esModule", { value: true }); -pattern$1.matchAny = pattern$1.convertPatternsToRe = pattern$1.makeRe = pattern$1.getPatternParts = pattern$1.expandBraceExpansion = pattern$1.expandPatternsWithBraceExpansion = pattern$1.isAffectDepthOfReadingPattern = pattern$1.endsWithSlashGlobStar = pattern$1.hasGlobStar = pattern$1.getBaseDirectory = pattern$1.isPatternRelatedToParentDirectory = pattern$1.getPatternsOutsideCurrentDirectory = pattern$1.getPatternsInsideCurrentDirectory = pattern$1.getPositivePatterns = pattern$1.getNegativePatterns = pattern$1.isPositivePattern = pattern$1.isNegativePattern = pattern$1.convertToNegativePattern = pattern$1.convertToPositivePattern = pattern$1.isDynamicPattern = pattern$1.isStaticPattern = void 0; -const path$f = require$$0$4; -const globParent$1 = globParent$2; -const micromatch = micromatch_1; -const GLOBSTAR$1 = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[[^[]*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\([^(]*\|[^|]*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\([^(]*\)/; -const BRACE_EXPANSION_SEPARATORS_RE = /,|\.\./; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -pattern$1.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && hasBraceExpansion(pattern)) { - return true; - } - return false; -} -pattern$1.isDynamicPattern = isDynamicPattern; -function hasBraceExpansion(pattern) { - const openingBraceIndex = pattern.indexOf('{'); - if (openingBraceIndex === -1) { - return false; - } - const closingBraceIndex = pattern.indexOf('}', openingBraceIndex + 1); - if (closingBraceIndex === -1) { - return false; - } - const braceContent = pattern.slice(openingBraceIndex, closingBraceIndex); - return BRACE_EXPANSION_SEPARATORS_RE.test(braceContent); -} -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -pattern$1.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -pattern$1.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -pattern$1.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -pattern$1.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -pattern$1.getNegativePatterns = getNegativePatterns; -function getPositivePatterns$1(patterns) { - return patterns.filter(isPositivePattern); -} -pattern$1.getPositivePatterns = getPositivePatterns$1; -/** - * Returns patterns that can be applied inside the current directory. - * - * @example - * // ['./*', '*', 'a/*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsInsideCurrentDirectory(patterns) { - return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); -} -pattern$1.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; -/** - * Returns patterns to be expanded relative to (outside) the current directory. - * - * @example - * // ['../*', './../*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsOutsideCurrentDirectory(patterns) { - return patterns.filter(isPatternRelatedToParentDirectory); -} -pattern$1.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; -function isPatternRelatedToParentDirectory(pattern) { - return pattern.startsWith('..') || pattern.startsWith('./..'); -} -pattern$1.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; -function getBaseDirectory(pattern) { - return globParent$1(pattern, { flipBackslashes: false }); -} -pattern$1.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR$1); -} -pattern$1.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR$1); -} -pattern$1.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path$f.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -pattern$1.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -pattern$1.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -pattern$1.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -pattern$1.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -pattern$1.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -pattern$1.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} +Object.defineProperty(pattern$1, "__esModule", { value: true }); +pattern$1.matchAny = pattern$1.convertPatternsToRe = pattern$1.makeRe = pattern$1.getPatternParts = pattern$1.expandBraceExpansion = pattern$1.expandPatternsWithBraceExpansion = pattern$1.isAffectDepthOfReadingPattern = pattern$1.endsWithSlashGlobStar = pattern$1.hasGlobStar = pattern$1.getBaseDirectory = pattern$1.isPatternRelatedToParentDirectory = pattern$1.getPatternsOutsideCurrentDirectory = pattern$1.getPatternsInsideCurrentDirectory = pattern$1.getPositivePatterns = pattern$1.getNegativePatterns = pattern$1.isPositivePattern = pattern$1.isNegativePattern = pattern$1.convertToNegativePattern = pattern$1.convertToPositivePattern = pattern$1.isDynamicPattern = pattern$1.isStaticPattern = void 0; +const path$f = require$$0$4; +const globParent$1 = globParent$2; +const micromatch = micromatch_1; +const GLOBSTAR$1 = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[[^[]*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\([^(]*\|[^|]*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\([^(]*\)/; +const BRACE_EXPANSION_SEPARATORS_RE = /,|\.\./; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +pattern$1.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * A special case with an empty string is necessary for matching patterns that start with a forward slash. + * An empty string cannot be a dynamic pattern. + * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. + */ + if (pattern === '') { + return false; + } + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && hasBraceExpansion(pattern)) { + return true; + } + return false; +} +pattern$1.isDynamicPattern = isDynamicPattern; +function hasBraceExpansion(pattern) { + const openingBraceIndex = pattern.indexOf('{'); + if (openingBraceIndex === -1) { + return false; + } + const closingBraceIndex = pattern.indexOf('}', openingBraceIndex + 1); + if (closingBraceIndex === -1) { + return false; + } + const braceContent = pattern.slice(openingBraceIndex, closingBraceIndex); + return BRACE_EXPANSION_SEPARATORS_RE.test(braceContent); +} +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +pattern$1.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +pattern$1.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +pattern$1.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +pattern$1.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +pattern$1.getNegativePatterns = getNegativePatterns; +function getPositivePatterns$1(patterns) { + return patterns.filter(isPositivePattern); +} +pattern$1.getPositivePatterns = getPositivePatterns$1; +/** + * Returns patterns that can be applied inside the current directory. + * + * @example + * // ['./*', '*', 'a/*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsInsideCurrentDirectory(patterns) { + return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); +} +pattern$1.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; +/** + * Returns patterns to be expanded relative to (outside) the current directory. + * + * @example + * // ['../*', './../*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsOutsideCurrentDirectory(patterns) { + return patterns.filter(isPatternRelatedToParentDirectory); +} +pattern$1.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; +function isPatternRelatedToParentDirectory(pattern) { + return pattern.startsWith('..') || pattern.startsWith('./..'); +} +pattern$1.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; +function getBaseDirectory(pattern) { + return globParent$1(pattern, { flipBackslashes: false }); +} +pattern$1.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR$1); +} +pattern$1.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR$1); +} +pattern$1.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path$f.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +pattern$1.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +pattern$1.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +pattern$1.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + /** + * The scan method returns an empty array in some cases. + * See micromatch/picomatch#58 for more details. + */ + if (parts.length === 0) { + parts = [pattern]; + } + /** + * The scan method does not return an empty part for the pattern with a forward slash. + * This is another part of micromatch/picomatch#58. + */ + if (parts[0].startsWith('/')) { + parts[0] = parts[0].slice(1); + parts.unshift(''); + } + return parts; +} +pattern$1.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +pattern$1.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +pattern$1.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} pattern$1.matchAny = matchAny; var stream$4 = {}; @@ -16323,154 +16323,154 @@ function pauseStreams (streams, options) { return streams } -Object.defineProperty(stream$4, "__esModule", { value: true }); -stream$4.merge = void 0; -const merge2 = merge2_1; -function merge$1(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -stream$4.merge = merge$1; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); +Object.defineProperty(stream$4, "__esModule", { value: true }); +stream$4.merge = void 0; +const merge2 = merge2_1; +function merge$1(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +stream$4.merge = merge$1; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); } var string$2 = {}; -Object.defineProperty(string$2, "__esModule", { value: true }); -string$2.isEmpty = string$2.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -string$2.isString = isString; -function isEmpty$1(input) { - return input === ''; -} +Object.defineProperty(string$2, "__esModule", { value: true }); +string$2.isEmpty = string$2.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +string$2.isString = isString; +function isEmpty$1(input) { + return input === ''; +} string$2.isEmpty = isEmpty$1; -Object.defineProperty(utils$g, "__esModule", { value: true }); -utils$g.string = utils$g.stream = utils$g.pattern = utils$g.path = utils$g.fs = utils$g.errno = utils$g.array = void 0; -const array = array$1; -utils$g.array = array; -const errno = errno$1; -utils$g.errno = errno; -const fs$g = fs$h; -utils$g.fs = fs$g; -const path$e = path$h; -utils$g.path = path$e; -const pattern = pattern$1; -utils$g.pattern = pattern; -const stream$3 = stream$4; -utils$g.stream = stream$3; -const string$1 = string$2; +Object.defineProperty(utils$g, "__esModule", { value: true }); +utils$g.string = utils$g.stream = utils$g.pattern = utils$g.path = utils$g.fs = utils$g.errno = utils$g.array = void 0; +const array = array$1; +utils$g.array = array; +const errno = errno$1; +utils$g.errno = errno; +const fs$g = fs$h; +utils$g.fs = fs$g; +const path$e = path$h; +utils$g.path = path$e; +const pattern = pattern$1; +utils$g.pattern = pattern; +const stream$3 = stream$4; +utils$g.stream = stream$3; +const string$1 = string$2; utils$g.string = string$1; -Object.defineProperty(tasks, "__esModule", { value: true }); -tasks.convertPatternGroupToTask = tasks.convertPatternGroupsToTasks = tasks.groupPatternsByBaseDirectory = tasks.getNegativePatternsAsPositive = tasks.getPositivePatterns = tasks.convertPatternsToTasks = tasks.generate = void 0; -const utils$a = utils$g; -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -tasks.generate = generate; -/** - * Returns tasks grouped by basic pattern directories. - * - * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. - * This is necessary because directory traversal starts at the base directory and goes deeper. - */ -function convertPatternsToTasks(positive, negative, dynamic) { - const tasks = []; - const patternsOutsideCurrentDirectory = utils$a.pattern.getPatternsOutsideCurrentDirectory(positive); - const patternsInsideCurrentDirectory = utils$a.pattern.getPatternsInsideCurrentDirectory(positive); - const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); - const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); - tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); - /* - * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory - * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. - */ - if ('.' in insideCurrentDirectoryGroup) { - tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); - } - else { - tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); - } - return tasks; -} -tasks.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils$a.pattern.getPositivePatterns(patterns); -} -tasks.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils$a.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils$a.pattern.convertToPositivePattern); - return positive; -} -tasks.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils$a.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -tasks.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -tasks.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils$a.pattern.convertToNegativePattern)) - }; -} +Object.defineProperty(tasks, "__esModule", { value: true }); +tasks.convertPatternGroupToTask = tasks.convertPatternGroupsToTasks = tasks.groupPatternsByBaseDirectory = tasks.getNegativePatternsAsPositive = tasks.getPositivePatterns = tasks.convertPatternsToTasks = tasks.generate = void 0; +const utils$a = utils$g; +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +tasks.generate = generate; +/** + * Returns tasks grouped by basic pattern directories. + * + * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. + * This is necessary because directory traversal starts at the base directory and goes deeper. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + const tasks = []; + const patternsOutsideCurrentDirectory = utils$a.pattern.getPatternsOutsideCurrentDirectory(positive); + const patternsInsideCurrentDirectory = utils$a.pattern.getPatternsInsideCurrentDirectory(positive); + const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); + const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); + tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); + /* + * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory + * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. + */ + if ('.' in insideCurrentDirectoryGroup) { + tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); + } + else { + tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); + } + return tasks; +} +tasks.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils$a.pattern.getPositivePatterns(patterns); +} +tasks.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils$a.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils$a.pattern.convertToPositivePattern); + return positive; +} +tasks.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils$a.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +tasks.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +tasks.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils$a.pattern.convertToNegativePattern)) + }; +} tasks.convertPatternGroupToTask = convertPatternGroupToTask; var patterns = {}; -Object.defineProperty(patterns, "__esModule", { value: true }); -patterns.removeDuplicateSlashes = patterns.transform = void 0; -/** - * Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string. - * The latter is due to the presence of the device path at the beginning of the UNC path. - * @todo rewrite to negative lookbehind with the next major release. - */ -const DOUBLE_SLASH_RE$1 = /(?!^)\/{2,}/g; -function transform(patterns) { - return patterns.map((pattern) => removeDuplicateSlashes(pattern)); -} -patterns.transform = transform; -/** - * This package only works with forward slashes as a path separator. - * Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes. - */ -function removeDuplicateSlashes(pattern) { - return pattern.replace(DOUBLE_SLASH_RE$1, '/'); -} +Object.defineProperty(patterns, "__esModule", { value: true }); +patterns.removeDuplicateSlashes = patterns.transform = void 0; +/** + * Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string. + * The latter is due to the presence of the device path at the beginning of the UNC path. + * @todo rewrite to negative lookbehind with the next major release. + */ +const DOUBLE_SLASH_RE$1 = /(?!^)\/{2,}/g; +function transform(patterns) { + return patterns.map((pattern) => removeDuplicateSlashes(pattern)); +} +patterns.transform = transform; +/** + * This package only works with forward slashes as a path separator. + * Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes. + */ +function removeDuplicateSlashes(pattern) { + return pattern.replace(DOUBLE_SLASH_RE$1, '/'); +} patterns.removeDuplicateSlashes = removeDuplicateSlashes; var async$7 = {}; @@ -17644,129 +17644,129 @@ function getSettings(settingsOrOptions = {}) { var reader = {}; -Object.defineProperty(reader, "__esModule", { value: true }); -const path$b = require$$0$4; -const fsStat$2 = out$1; -const utils$6 = utils$g; -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat$2.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path$b.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils$6.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils$6.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} +Object.defineProperty(reader, "__esModule", { value: true }); +const path$b = require$$0$4; +const fsStat$2 = out$1; +const utils$6 = utils$g; +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat$2.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path$b.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils$6.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils$6.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} reader.default = Reader; var stream$1 = {}; -Object.defineProperty(stream$1, "__esModule", { value: true }); -const stream_1$3 = require$$0$7; -const fsStat$1 = out$1; -const fsWalk$2 = out$3; -const reader_1$2 = reader; -class ReaderStream extends reader_1$2.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk$2.walkStream; - this._stat = fsStat$1.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1$3.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} +Object.defineProperty(stream$1, "__esModule", { value: true }); +const stream_1$3 = require$$0$7; +const fsStat$1 = out$1; +const fsWalk$2 = out$3; +const reader_1$2 = reader; +class ReaderStream extends reader_1$2.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk$2.walkStream; + this._stat = fsStat$1.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1$3.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} stream$1.default = ReaderStream; -Object.defineProperty(async$6, "__esModule", { value: true }); -const fsWalk$1 = out$3; -const reader_1$1 = reader; -const stream_1$2 = stream$1; -class ReaderAsync extends reader_1$1.default { - constructor() { - super(...arguments); - this._walkAsync = fsWalk$1.walk; - this._readerStream = new stream_1$2.default(this._settings); - } - dynamic(root, options) { - return new Promise((resolve, reject) => { - this._walkAsync(root, options, (error, entries) => { - if (error === null) { - resolve(entries); - } - else { - reject(error); - } - }); - }); - } - async static(patterns, options) { - const entries = []; - const stream = this._readerStream.static(patterns, options); - // After #235, replace it with an asynchronous iterator. - return new Promise((resolve, reject) => { - stream.once('error', reject); - stream.on('data', (entry) => entries.push(entry)); - stream.once('end', () => resolve(entries)); - }); - } -} +Object.defineProperty(async$6, "__esModule", { value: true }); +const fsWalk$1 = out$3; +const reader_1$1 = reader; +const stream_1$2 = stream$1; +class ReaderAsync extends reader_1$1.default { + constructor() { + super(...arguments); + this._walkAsync = fsWalk$1.walk; + this._readerStream = new stream_1$2.default(this._settings); + } + dynamic(root, options) { + return new Promise((resolve, reject) => { + this._walkAsync(root, options, (error, entries) => { + if (error === null) { + resolve(entries); + } + else { + reject(error); + } + }); + }); + } + async static(patterns, options) { + const entries = []; + const stream = this._readerStream.static(patterns, options); + // After #235, replace it with an asynchronous iterator. + return new Promise((resolve, reject) => { + stream.once('error', reject); + stream.on('data', (entry) => entries.push(entry)); + stream.once('end', () => resolve(entries)); + }); + } +} async$6.default = ReaderAsync; var provider = {}; @@ -17777,568 +17777,568 @@ var partial = {}; var matcher = {}; -Object.defineProperty(matcher, "__esModule", { value: true }); -const utils$5 = utils$g; -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils$5.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils$5.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils$5.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils$5.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils$5.array.splitWhen(segments, (segment) => segment.dynamic && utils$5.pattern.hasGlobStar(segment.pattern)); - } -} +Object.defineProperty(matcher, "__esModule", { value: true }); +const utils$5 = utils$g; +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils$5.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils$5.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils$5.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils$5.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils$5.array.splitWhen(segments, (segment) => segment.dynamic && utils$5.pattern.hasGlobStar(segment.pattern)); + } +} matcher.default = Matcher; -Object.defineProperty(partial, "__esModule", { value: true }); -const matcher_1 = matcher; -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} +Object.defineProperty(partial, "__esModule", { value: true }); +const matcher_1 = matcher; +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} partial.default = PartialMatcher; -Object.defineProperty(deep, "__esModule", { value: true }); -const utils$4 = utils$g; -const partial_1 = partial; -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils$4.pattern.isAffectDepthOfReadingPattern); - return utils$4.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils$4.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils$4.pattern.matchAny(entryPath, patternsRe); - } -} +Object.defineProperty(deep, "__esModule", { value: true }); +const utils$4 = utils$g; +const partial_1 = partial; +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils$4.pattern.isAffectDepthOfReadingPattern); + return utils$4.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils$4.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils$4.pattern.matchAny(entryPath, patternsRe); + } +} deep.default = DeepFilter; var entry$1 = {}; -Object.defineProperty(entry$1, "__esModule", { value: true }); -const utils$3 = utils$g; -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils$3.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils$3.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isDirectory = entry.dirent.isDirectory(); - const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(entry.path, negativeRe, isDirectory); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils$3.path.makeAbsolute(this._settings.cwd, entryPath); - return utils$3.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe, isDirectory) { - const filepath = utils$3.path.removeLeadingDotSegment(entryPath); - // Trying to match files and directories by patterns. - const isMatched = utils$3.pattern.matchAny(filepath, patternsRe); - // A pattern with a trailling slash can be used for directory matching. - // To apply such pattern, we need to add a tralling slash to the path. - if (!isMatched && isDirectory) { - return utils$3.pattern.matchAny(filepath + '/', patternsRe); - } - return isMatched; - } -} +Object.defineProperty(entry$1, "__esModule", { value: true }); +const utils$3 = utils$g; +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils$3.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils$3.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique && this._isDuplicateEntry(entry)) { + return false; + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + const isDirectory = entry.dirent.isDirectory(); + const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(entry.path, negativeRe, isDirectory); + if (this._settings.unique && isMatched) { + this._createIndexRecord(entry); + } + return isMatched; + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils$3.path.makeAbsolute(this._settings.cwd, entryPath); + return utils$3.pattern.matchAny(fullpath, patternsRe); + } + _isMatchToPatterns(entryPath, patternsRe, isDirectory) { + const filepath = utils$3.path.removeLeadingDotSegment(entryPath); + // Trying to match files and directories by patterns. + const isMatched = utils$3.pattern.matchAny(filepath, patternsRe); + // A pattern with a trailling slash can be used for directory matching. + // To apply such pattern, we need to add a tralling slash to the path. + if (!isMatched && isDirectory) { + return utils$3.pattern.matchAny(filepath + '/', patternsRe); + } + return isMatched; + } +} entry$1.default = EntryFilter; var error$2 = {}; -Object.defineProperty(error$2, "__esModule", { value: true }); -const utils$2 = utils$g; -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils$2.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} +Object.defineProperty(error$2, "__esModule", { value: true }); +const utils$2 = utils$g; +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils$2.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} error$2.default = ErrorFilter; var entry = {}; -Object.defineProperty(entry, "__esModule", { value: true }); -const utils$1 = utils$g; -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils$1.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils$1.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} +Object.defineProperty(entry, "__esModule", { value: true }); +const utils$1 = utils$g; +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils$1.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils$1.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} entry.default = EntryTransformer; -Object.defineProperty(provider, "__esModule", { value: true }); -const path$a = require$$0$4; -const deep_1 = deep; -const entry_1 = entry$1; -const error_1 = error$2; -const entry_2 = entry; -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path$a.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} +Object.defineProperty(provider, "__esModule", { value: true }); +const path$a = require$$0$4; +const deep_1 = deep; +const entry_1 = entry$1; +const error_1 = error$2; +const entry_2 = entry; +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path$a.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} provider.default = Provider; -Object.defineProperty(async$7, "__esModule", { value: true }); -const async_1$1 = async$6; -const provider_1$2 = provider; -class ProviderAsync extends provider_1$2.default { - constructor() { - super(...arguments); - this._reader = new async_1$1.default(this._settings); - } - async read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = await this.api(root, task, options); - return entries.map((entry) => options.transform(entry)); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} +Object.defineProperty(async$7, "__esModule", { value: true }); +const async_1$1 = async$6; +const provider_1$2 = provider; +class ProviderAsync extends provider_1$2.default { + constructor() { + super(...arguments); + this._reader = new async_1$1.default(this._settings); + } + async read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = await this.api(root, task, options); + return entries.map((entry) => options.transform(entry)); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} async$7.default = ProviderAsync; var stream = {}; -Object.defineProperty(stream, "__esModule", { value: true }); -const stream_1$1 = require$$0$7; -const stream_2 = stream$1; -const provider_1$1 = provider; -class ProviderStream extends provider_1$1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1$1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} +Object.defineProperty(stream, "__esModule", { value: true }); +const stream_1$1 = require$$0$7; +const stream_2 = stream$1; +const provider_1$1 = provider; +class ProviderStream extends provider_1$1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1$1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} stream.default = ProviderStream; var sync$2 = {}; var sync$1 = {}; -Object.defineProperty(sync$1, "__esModule", { value: true }); -const fsStat = out$1; -const fsWalk = out$3; -const reader_1 = reader; -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} +Object.defineProperty(sync$1, "__esModule", { value: true }); +const fsStat = out$1; +const fsWalk = out$3; +const reader_1 = reader; +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; + } + dynamic(root, options) { + return this._walkSync(root, options); + } + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; + } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} sync$1.default = ReaderSync; -Object.defineProperty(sync$2, "__esModule", { value: true }); -const sync_1$1 = sync$1; -const provider_1 = provider; -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1$1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} +Object.defineProperty(sync$2, "__esModule", { value: true }); +const sync_1$1 = sync$1; +const provider_1 = provider; +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1$1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} sync$2.default = ProviderSync; var settings = {}; (function (exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; - const fs = require$$0__default; - const os = require$$2; - /** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ - const CPU_COUNT = Math.max(os.cpus().length, 1); - exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync - }; - class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } - } + Object.defineProperty(exports, "__esModule", { value: true }); + exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; + const fs = require$$0__default; + const os = require$$2; + /** + * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. + * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 + */ + const CPU_COUNT = Math.max(os.cpus().length, 1); + exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync + }; + class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; + } + if (this.stats) { + this.objectMode = true; + } + } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } + } exports.default = Settings; } (settings)); -const taskManager = tasks; -const patternManager = patterns; -const async_1 = async$7; -const stream_1 = stream; -const sync_1 = sync$2; -const settings_1 = settings; -const utils = utils$g; -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = patternManager.transform([].concat(source)); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = patternManager.transform([].concat(source)); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} +const taskManager = tasks; +const patternManager = patterns; +const async_1 = async$7; +const stream_1 = stream; +const sync_1 = sync$2; +const settings_1 = settings; +const utils = utils$g; +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = patternManager.transform([].concat(source)); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = patternManager.transform([].concat(source)); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} var out = FastGlob; var dist = {}; diff --git a/frontend/node_modules/vite/dist/node/chunks/dep-9deb2354.js b/frontend/node_modules/vite/dist/node/chunks/dep-9deb2354.js index d4c8490..5fb2bbe 100644 --- a/frontend/node_modules/vite/dist/node/chunks/dep-9deb2354.js +++ b/frontend/node_modules/vite/dist/node/chunks/dep-9deb2354.js @@ -159,83 +159,83 @@ var pify$1 = pify$2.exports = function (obj, P, opts) { pify$1.all = pify$1; -var fs = require$$0__default; -var path$2 = require$$0; -var pify = pify$2.exports; - -var stat = pify(fs.stat); -var readFile = pify(fs.readFile); -var resolve = path$2.resolve; - -var cache = Object.create(null); - -function convert(content, encoding) { - if (Buffer.isEncoding(encoding)) { - return content.toString(encoding); - } - return content; -} - -readCache$1.exports = function (path, encoding) { - path = resolve(path); - - return stat(path).then(function (stats) { - var item = cache[path]; - - if (item && item.mtime.getTime() === stats.mtime.getTime()) { - return convert(item.content, encoding); - } - - return readFile(path).then(function (data) { - cache[path] = { - mtime: stats.mtime, - content: data - }; - - return convert(data, encoding); - }); - }).catch(function (err) { - cache[path] = null; - return Promise.reject(err); - }); -}; - -readCache$1.exports.sync = function (path, encoding) { - path = resolve(path); - - try { - var stats = fs.statSync(path); - var item = cache[path]; - - if (item && item.mtime.getTime() === stats.mtime.getTime()) { - return convert(item.content, encoding); - } - - var data = fs.readFileSync(path); - - cache[path] = { - mtime: stats.mtime, - content: data - }; - - return convert(data, encoding); - } catch (err) { - cache[path] = null; - throw err; - } - -}; - -readCache$1.exports.get = function (path, encoding) { - path = resolve(path); - if (cache[path]) { - return convert(cache[path].content, encoding); - } - return null; -}; - -readCache$1.exports.clear = function () { - cache = Object.create(null); +var fs = require$$0__default; +var path$2 = require$$0; +var pify = pify$2.exports; + +var stat = pify(fs.stat); +var readFile = pify(fs.readFile); +var resolve = path$2.resolve; + +var cache = Object.create(null); + +function convert(content, encoding) { + if (Buffer.isEncoding(encoding)) { + return content.toString(encoding); + } + return content; +} + +readCache$1.exports = function (path, encoding) { + path = resolve(path); + + return stat(path).then(function (stats) { + var item = cache[path]; + + if (item && item.mtime.getTime() === stats.mtime.getTime()) { + return convert(item.content, encoding); + } + + return readFile(path).then(function (data) { + cache[path] = { + mtime: stats.mtime, + content: data + }; + + return convert(data, encoding); + }); + }).catch(function (err) { + cache[path] = null; + return Promise.reject(err); + }); +}; + +readCache$1.exports.sync = function (path, encoding) { + path = resolve(path); + + try { + var stats = fs.statSync(path); + var item = cache[path]; + + if (item && item.mtime.getTime() === stats.mtime.getTime()) { + return convert(item.content, encoding); + } + + var data = fs.readFileSync(path); + + cache[path] = { + mtime: stats.mtime, + content: data + }; + + return convert(data, encoding); + } catch (err) { + cache[path] = null; + throw err; + } + +}; + +readCache$1.exports.get = function (path, encoding) { + path = resolve(path); + if (cache[path]) { + return convert(cache[path].content, encoding); + } + return null; +}; + +readCache$1.exports.clear = function () { + cache = Object.create(null); }; const readCache = readCache$1.exports; diff --git a/frontend/node_modules/vite/dist/node/index.d.ts b/frontend/node_modules/vite/dist/node/index.d.ts index 1dfeef0..d35b7fe 100644 --- a/frontend/node_modules/vite/dist/node/index.d.ts +++ b/frontend/node_modules/vite/dist/node/index.d.ts @@ -1,3248 +1,3248 @@ -/// - -import type { Agent } from 'node:http'; -import type { BuildOptions as BuildOptions_2 } from 'esbuild'; -import type { ClientRequest } from 'node:http'; -import type { ClientRequestArgs } from 'node:http'; -import { ConnectedPayload } from "../../types/hmrPayload"; -import { CustomEventMap } from "../../types/customEvent"; -import { CustomPayload } from "../../types/hmrPayload"; -import type { CustomPluginOptions } from 'rollup'; -import type { Duplex } from 'node:stream'; -import type { DuplexOptions } from 'node:stream'; -import { ErrorPayload } from "../../types/hmrPayload"; -import { TransformOptions as EsbuildTransformOptions } from 'esbuild'; -import { version as esbuildVersion } from 'esbuild'; -import { EventEmitter } from 'node:events'; -import * as events from 'node:events'; -import type { ExistingRawSourceMap } from 'rollup'; -import type * as fs from 'node:fs'; -import { FullReloadPayload } from "../../types/hmrPayload"; -import { GeneralImportGlobOptions } from "../../types/importGlob"; -import type { GetManualChunk } from 'rollup'; -import { HMRPayload } from "../../types/hmrPayload"; -import * as http from 'node:http'; -import { ImportGlobEagerFunction } from "../../types/importGlob"; -import { ImportGlobFunction } from "../../types/importGlob"; -import { ImportGlobOptions } from "../../types/importGlob"; -import type { IncomingMessage } from 'node:http'; -import { InferCustomEventPayload } from "../../types/customEvent"; -import type { InputOption } from 'rollup'; -import type { InputOptions } from 'rollup'; -import { InvalidatePayload } from "../../types/customEvent"; -import { KnownAsTypeMap } from "../../types/importGlob"; -import type { LoadResult } from 'rollup'; - -import type { ModuleFormat } from 'rollup'; -import type { ModuleInfo } from 'rollup'; -import type * as net from 'node:net'; -import type { ObjectHook } from 'rollup'; -import type { OutgoingHttpHeaders } from 'node:http'; -import type { OutputBundle } from 'rollup'; -import type { OutputChunk } from 'rollup'; -import type { PartialResolvedId } from 'rollup'; -import type { Plugin as Plugin_3 } from 'rollup'; -import type { PluginContext } from 'rollup'; -import type { PluginHooks } from 'rollup'; -import type * as PostCSS from 'postcss'; -import { PrunePayload } from "../../types/hmrPayload"; -import type { ResolveIdResult } from 'rollup'; -import type { RollupError } from 'rollup'; -import type { RollupOptions } from 'rollup'; -import type { RollupOutput } from 'rollup'; -import { VERSION as rollupVersion } from 'rollup'; -import type { RollupWatcher } from 'rollup'; -import type { SecureContextOptions } from 'node:tls'; -import type { Server } from 'node:http'; -import type { Server as Server_2 } from 'node:https'; -import type { ServerOptions as ServerOptions_2 } from 'node:https'; -import type { ServerResponse } from 'node:http'; -import type { SourceDescription } from 'rollup'; -import type { SourceMap } from 'rollup'; -import type { SourceMapInput } from 'rollup'; -import type * as stream from 'node:stream'; -import type { TransformPluginContext } from 'rollup'; -import type { TransformResult as TransformResult_2 } from 'rollup'; -import type { TransformResult as TransformResult_3 } from 'esbuild'; -import { Update } from "../../types/hmrPayload"; -import { UpdatePayload } from "../../types/hmrPayload"; -import type * as url from 'node:url'; -import type { URL as URL_2 } from 'node:url'; -import type { WatcherOptions } from 'rollup'; -import type { ZlibOptions } from 'node:zlib'; - -export declare interface Alias { - find: string | RegExp - replacement: string - /** - * Instructs the plugin to use an alternative resolving algorithm, - * rather than the Rollup's resolver. - * @default null - */ - customResolver?: ResolverFunction | ResolverObject | null -} - -/** - * Specifies an `Object`, or an `Array` of `Object`, - * which defines aliases used to replace values in `import` or `require` statements. - * With either format, the order of the entries is important, - * in that the first defined rules are applied first. - * - * This is passed to \@rollup/plugin-alias as the "entries" field - * https://github.com/rollup/plugins/tree/master/packages/alias#entries - */ -export declare type AliasOptions = readonly Alias[] | { [find: string]: string } - -export declare type AnymatchFn = (testString: string) => boolean - -export declare type AnymatchPattern = string | RegExp | AnymatchFn - -/** - * spa: include SPA fallback middleware and configure sirv with `single: true` in preview - * - * mpa: only include non-SPA HTML middlewares - * - * custom: don't include HTML middlewares - */ -export declare type AppType = 'spa' | 'mpa' | 'custom'; - -export declare interface AwaitWriteFinishOptions { - /** - * Amount of time in milliseconds for a file size to remain constant before emitting its event. - */ - stabilityThreshold?: number - - /** - * File size polling interval. - */ - pollInterval?: number -} - -/** - * Bundles the app for production. - * Returns a Promise containing the build result. - */ -export declare function build(inlineConfig?: InlineConfig): Promise; - -export declare interface BuildOptions { - /** - * Compatibility transform target. The transform is performed with esbuild - * and the lowest supported target is es2015/es6. Note this only handles - * syntax transformation and does not cover polyfills (except for dynamic - * import) - * - * Default: 'modules' - Similar to `@babel/preset-env`'s targets.esmodules, - * transpile targeting browsers that natively support dynamic es module imports. - * https://caniuse.com/es6-module-dynamic-import - * - * Another special value is 'esnext' - which only performs minimal transpiling - * (for minification compat) and assumes native dynamic imports support. - * - * For custom targets, see https://esbuild.github.io/api/#target and - * https://esbuild.github.io/content-types/#javascript for more details. - */ - target?: 'modules' | EsbuildTransformOptions['target'] | false; - /** - * whether to inject module preload polyfill. - * Note: does not apply to library mode. - * @default true - * @deprecated use `modulePreload.polyfill` instead - */ - polyfillModulePreload?: boolean; - /** - * Configure module preload - * Note: does not apply to library mode. - * @default true - */ - modulePreload?: boolean | ModulePreloadOptions; - /** - * Directory relative from `root` where build output will be placed. If the - * directory exists, it will be removed before the build. - * @default 'dist' - */ - outDir?: string; - /** - * Directory relative from `outDir` where the built js/css/image assets will - * be placed. - * @default 'assets' - */ - assetsDir?: string; - /** - * Static asset files smaller than this number (in bytes) will be inlined as - * base64 strings. Default limit is `4096` (4kb). Set to `0` to disable. - * @default 4096 - */ - assetsInlineLimit?: number; - /** - * Whether to code-split CSS. When enabled, CSS in async chunks will be - * inlined as strings in the chunk and inserted via dynamically created - * style tags when the chunk is loaded. - * @default true - */ - cssCodeSplit?: boolean; - /** - * An optional separate target for CSS minification. - * As esbuild only supports configuring targets to mainstream - * browsers, users may need this option when they are targeting - * a niche browser that comes with most modern JavaScript features - * but has poor CSS support, e.g. Android WeChat WebView, which - * doesn't support the #RGBA syntax. - */ - cssTarget?: EsbuildTransformOptions['target'] | false; - /** - * If `true`, a separate sourcemap file will be created. If 'inline', the - * sourcemap will be appended to the resulting output file as data URI. - * 'hidden' works like `true` except that the corresponding sourcemap - * comments in the bundled files are suppressed. - * @default false - */ - sourcemap?: boolean | 'inline' | 'hidden'; - /** - * Set to `false` to disable minification, or specify the minifier to use. - * Available options are 'terser' or 'esbuild'. - * @default 'esbuild' - */ - minify?: boolean | 'terser' | 'esbuild'; - /** - * Options for terser - * https://terser.org/docs/api-reference#minify-options - */ - terserOptions?: Terser.MinifyOptions; - /** - * Will be merged with internal rollup options. - * https://rollupjs.org/guide/en/#big-list-of-options - */ - rollupOptions?: RollupOptions; - /** - * Options to pass on to `@rollup/plugin-commonjs` - */ - commonjsOptions?: RollupCommonJSOptions; - /** - * Options to pass on to `@rollup/plugin-dynamic-import-vars` - */ - dynamicImportVarsOptions?: RollupDynamicImportVarsOptions; - /** - * Whether to write bundle to disk - * @default true - */ - write?: boolean; - /** - * Empty outDir on write. - * @default true when outDir is a sub directory of project root - */ - emptyOutDir?: boolean | null; - /** - * Copy the public directory to outDir on write. - * @default true - * @experimental - */ - copyPublicDir?: boolean; - /** - * Whether to emit a manifest.json under assets dir to map hash-less filenames - * to their hashed versions. Useful when you want to generate your own HTML - * instead of using the one generated by Vite. - * - * Example: - * - * ```json - * { - * "main.js": { - * "file": "main.68fe3fad.js", - * "css": "main.e6b63442.css", - * "imports": [...], - * "dynamicImports": [...] - * } - * } - * ``` - * @default false - */ - manifest?: boolean | string; - /** - * Build in library mode. The value should be the global name of the lib in - * UMD mode. This will produce esm + cjs + umd bundle formats with default - * configurations that are suitable for distributing libraries. - */ - lib?: LibraryOptions | false; - /** - * Produce SSR oriented build. Note this requires specifying SSR entry via - * `rollupOptions.input`. - */ - ssr?: boolean | string; - /** - * Generate SSR manifest for determining style links and asset preload - * directives in production. - */ - ssrManifest?: boolean | string; - /** - * Set to false to disable reporting compressed chunk sizes. - * Can slightly improve build speed. - */ - reportCompressedSize?: boolean; - /** - * Adjust chunk size warning limit (in kbs). - * @default 500 - */ - chunkSizeWarningLimit?: number; - /** - * Rollup watch options - * https://rollupjs.org/guide/en/#watchoptions - */ - watch?: WatcherOptions | null; -} - -export declare interface ChunkMetadata { - importedAssets: Set; - importedCss: Set; -} - -export declare interface CommonServerOptions { - /** - * Specify server port. Note if the port is already being used, Vite will - * automatically try the next available port so this may not be the actual - * port the server ends up listening on. - */ - port?: number; - /** - * If enabled, vite will exit if specified port is already in use - */ - strictPort?: boolean; - /** - * Specify which IP addresses the server should listen on. - * Set to 0.0.0.0 to listen on all addresses, including LAN and public addresses. - */ - host?: string | boolean; - /** - * Enable TLS + HTTP/2. - * Note: this downgrades to TLS only when the proxy option is also used. - */ - https?: boolean | ServerOptions_2; - /** - * Open browser window on startup - */ - open?: boolean | string; - /** - * Configure custom proxy rules for the dev server. Expects an object - * of `{ key: options }` pairs. - * Uses [`http-proxy`](https://github.com/http-party/node-http-proxy). - * Full options [here](https://github.com/http-party/node-http-proxy#options). - * - * Example `vite.config.js`: - * ``` js - * module.exports = { - * proxy: { - * // string shorthand - * '/foo': 'http://localhost:4567/foo', - * // with options - * '/api': { - * target: 'http://jsonplaceholder.typicode.com', - * changeOrigin: true, - * rewrite: path => path.replace(/^\/api/, '') - * } - * } - * } - * ``` - */ - proxy?: Record; - /** - * Configure CORS for the dev server. - * Uses https://github.com/expressjs/cors. - * Set to `true` to allow all methods from any origin, or configure separately - * using an object. - */ - cors?: CorsOptions | boolean; - /** - * Specify server response headers. - */ - headers?: OutgoingHttpHeaders; -} - -export declare interface ConfigEnv { - command: 'build' | 'serve'; - mode: string; - /** - * @experimental - */ - ssrBuild?: boolean; -} - -export declare namespace Connect { - export type ServerHandle = HandleFunction | http.Server - - export class IncomingMessage extends http.IncomingMessage { - originalUrl?: http.IncomingMessage['url'] | undefined - } - - export type NextFunction = (err?: any) => void - - export type SimpleHandleFunction = ( - req: IncomingMessage, - res: http.ServerResponse - ) => void - export type NextHandleFunction = ( - req: IncomingMessage, - res: http.ServerResponse, - next: NextFunction - ) => void - export type ErrorHandleFunction = ( - err: any, - req: IncomingMessage, - res: http.ServerResponse, - next: NextFunction - ) => void - export type HandleFunction = - | SimpleHandleFunction - | NextHandleFunction - | ErrorHandleFunction - - export interface ServerStackItem { - route: string - handle: ServerHandle - } - - export interface Server extends NodeJS.EventEmitter { - (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void - - route: string - stack: ServerStackItem[] - - /** - * Utilize the given middleware `handle` to the given `route`, - * defaulting to _/_. This "route" is the mount-point for the - * middleware, when given a value other than _/_ the middleware - * is only effective when that segment is present in the request's - * pathname. - * - * For example if we were to mount a function at _/admin_, it would - * be invoked on _/admin_, and _/admin/settings_, however it would - * not be invoked for _/_, or _/posts_. - */ - use(fn: NextHandleFunction): Server - use(fn: HandleFunction): Server - use(route: string, fn: NextHandleFunction): Server - use(route: string, fn: HandleFunction): Server - - /** - * Handle server requests, punting them down - * the middleware stack. - */ - handle( - req: http.IncomingMessage, - res: http.ServerResponse, - next: Function - ): void - - /** - * Listen for connections. - * - * This method takes the same arguments - * as node's `http.Server#listen()`. - * - * HTTP and HTTPS: - * - * If you run your application both as HTTP - * and HTTPS you may wrap them individually, - * since your Connect "server" is really just - * a JavaScript `Function`. - * - * var connect = require('connect') - * , http = require('http') - * , https = require('https'); - * - * var app = connect(); - * - * http.createServer(app).listen(80); - * https.createServer(options, app).listen(443); - */ - listen( - port: number, - hostname?: string, - backlog?: number, - callback?: Function - ): http.Server - listen(port: number, hostname?: string, callback?: Function): http.Server - listen(path: string, callback?: Function): http.Server - listen(handle: any, listeningListener?: Function): http.Server - } -} - -export { ConnectedPayload } - -/** - * https://github.com/expressjs/cors#configuration-options - */ -export declare interface CorsOptions { - origin?: CorsOrigin | ((origin: string, cb: (err: Error, origins: CorsOrigin) => void) => void); - methods?: string | string[]; - allowedHeaders?: string | string[]; - exposedHeaders?: string | string[]; - credentials?: boolean; - maxAge?: number; - preflightContinue?: boolean; - optionsSuccessStatus?: number; -} - -export declare type CorsOrigin = boolean | string | RegExp | (string | RegExp)[]; - -export declare const createFilter: (include?: FilterPattern | undefined, exclude?: FilterPattern | undefined, options?: { - resolve?: string | false | null | undefined; -} | undefined) => (id: string | unknown) => boolean; - -export declare function createLogger(level?: LogLevel, options?: LoggerOptions): Logger; - -export declare function createServer(inlineConfig?: InlineConfig): Promise; - -export declare interface CSSModulesOptions { - getJSON?: (cssFileName: string, json: Record, outputFileName: string) => void; - scopeBehaviour?: 'global' | 'local'; - globalModulePaths?: RegExp[]; - generateScopedName?: string | ((name: string, filename: string, css: string) => string); - hashPrefix?: string; - /** - * default: null - */ - localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' | null; -} - -export declare interface CSSOptions { - /** - * https://github.com/css-modules/postcss-modules - */ - modules?: CSSModulesOptions | false; - preprocessorOptions?: Record; - postcss?: string | (PostCSS.ProcessOptions & { - plugins?: PostCSS.AcceptedPlugin[]; - }); - /** - * Enables css sourcemaps during dev - * @default false - * @experimental - */ - devSourcemap?: boolean; -} - -export { CustomEventMap } - -export { CustomPayload } - -/** - * Type helper to make it easier to use vite.config.ts - * accepts a direct {@link UserConfig} object, or a function that returns it. - * The function receives a {@link ConfigEnv} object that exposes two properties: - * `command` (either `'build'` or `'serve'`), and `mode`. - */ -export declare function defineConfig(config: UserConfigExport): UserConfigExport; - -export declare interface DepOptimizationConfig { - /** - * Force optimize listed dependencies (must be resolvable import paths, - * cannot be globs). - */ - include?: string[]; - /** - * Do not optimize these dependencies (must be resolvable import paths, - * cannot be globs). - */ - exclude?: string[]; - /** - * Force ESM interop when importing for these dependencies. Some legacy - * packages advertise themselves as ESM but use `require` internally - * @experimental - */ - needsInterop?: string[]; - /** - * Options to pass to esbuild during the dep scanning and optimization - * - * Certain options are omitted since changing them would not be compatible - * with Vite's dep optimization. - * - * - `external` is also omitted, use Vite's `optimizeDeps.exclude` option - * - `plugins` are merged with Vite's dep plugin - * - * https://esbuild.github.io/api - */ - esbuildOptions?: Omit; - /** - * List of file extensions that can be optimized. A corresponding esbuild - * plugin must exist to handle the specific extension. - * - * By default, Vite can optimize `.mjs`, `.js`, `.ts`, and `.mts` files. This option - * allows specifying additional extensions. - * - * @experimental - */ - extensions?: string[]; - /** - * Disables dependencies optimizations, true disables the optimizer during - * build and dev. Pass 'build' or 'dev' to only disable the optimizer in - * one of the modes. Deps optimization is enabled by default in dev only. - * @default 'build' - * @experimental - */ - disabled?: boolean | 'build' | 'dev'; -} - -export declare interface DepOptimizationMetadata { - /** - * The main hash is determined by user config and dependency lockfiles. - * This is checked on server startup to avoid unnecessary re-bundles. - */ - hash: string; - /** - * The browser hash is determined by the main hash plus additional dependencies - * discovered at runtime. This is used to invalidate browser requests to - * optimized deps. - */ - browserHash: string; - /** - * Metadata for each already optimized dependency - */ - optimized: Record; - /** - * Metadata for non-entry optimized chunks and dynamic imports - */ - chunks: Record; - /** - * Metadata for each newly discovered dependency after processing - */ - discovered: Record; - /** - * OptimizedDepInfo list - */ - depInfoList: OptimizedDepInfo[]; -} - -export declare type DepOptimizationOptions = DepOptimizationConfig & { - /** - * By default, Vite will crawl your `index.html` to detect dependencies that - * need to be pre-bundled. If `build.rollupOptions.input` is specified, Vite - * will crawl those entry points instead. - * - * If neither of these fit your needs, you can specify custom entries using - * this option - the value should be a fast-glob pattern or array of patterns - * (https://github.com/mrmlnc/fast-glob#basic-syntax) that are relative from - * vite project root. This will overwrite default entries inference. - */ - entries?: string | string[]; - /** - * Force dep pre-optimization regardless of whether deps have changed. - * @experimental - */ - force?: boolean; -}; - -export declare interface DepOptimizationProcessing { - promise: Promise; - resolve: () => void; -} - -export declare interface DepOptimizationResult { - metadata: DepOptimizationMetadata; - /** - * When doing a re-run, if there are newly discovered dependencies - * the page reload will be delayed until the next rerun so we need - * to be able to discard the result - */ - commit: () => Promise; - cancel: () => void; -} - -export declare interface DepsOptimizer { - metadata: DepOptimizationMetadata; - scanProcessing?: Promise; - registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo; - run: () => void; - isOptimizedDepFile: (id: string) => boolean; - isOptimizedDepUrl: (url: string) => boolean; - getOptimizedDepId: (depInfo: OptimizedDepInfo) => string; - delayDepsOptimizerUntil: (id: string, done: () => Promise) => void; - registerWorkersSource: (id: string) => void; - resetRegisteredIds: () => void; - ensureFirstRun: () => void; - close: () => Promise; - options: DepOptimizationOptions; -} - -export { ErrorPayload } - -export declare interface ESBuildOptions extends EsbuildTransformOptions { - include?: string | RegExp | string[] | RegExp[]; - exclude?: string | RegExp | string[] | RegExp[]; - jsxInject?: string; - /** - * This option is not respected. Use `build.minify` instead. - */ - minify?: never; -} - -export { EsbuildTransformOptions } - -export declare type ESBuildTransformResult = Omit & { - map: SourceMap; -}; - -export { esbuildVersion } - -export declare interface ExperimentalOptions { - /** - * Append fake `&lang.(ext)` when queries are specified, to preserve the file extension for following plugins to process. - * - * @experimental - * @default false - */ - importGlobRestoreExtension?: boolean; - /** - * Allow finegrain control over assets and public files paths - * - * @experimental - */ - renderBuiltUrl?: RenderBuiltAssetUrl; - /** - * Enables support of HMR partial accept via `import.meta.hot.acceptExports`. - * - * @experimental - * @default false - */ - hmrPartialAccept?: boolean; -} - -export declare type ExportsData = { - hasImports: boolean; - exports: readonly string[]; - facade: boolean; - hasReExports?: boolean; - jsxLoader?: boolean; -}; - -export declare interface FileSystemServeOptions { - /** - * Strictly restrict file accessing outside of allowing paths. - * - * Set to `false` to disable the warning - * - * @default true - */ - strict?: boolean; - /** - * Restrict accessing files outside the allowed directories. - * - * Accepts absolute path or a path relative to project root. - * Will try to search up for workspace root by default. - */ - allow?: string[]; - /** - * Restrict accessing files that matches the patterns. - * - * This will have higher priority than `allow`. - * picomatch patterns are supported. - * - * @default ['.env', '.env.*', '*.crt', '*.pem'] - */ - deny?: string[]; -} - -/** - * Inlined to keep `@rollup/pluginutils` in devDependencies - */ -export declare type FilterPattern = ReadonlyArray | string | RegExp | null; - -export declare function formatPostcssSourceMap(rawMap: ExistingRawSourceMap, file: string): Promise; - -export declare class FSWatcher extends EventEmitter implements fs.FSWatcher { - options: WatchOptions - - /** - * Constructs a new FSWatcher instance with optional WatchOptions parameter. - */ - constructor(options?: WatchOptions) - - /** - * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one - * string. - */ - add(paths: string | ReadonlyArray): this - - /** - * Stop watching files, directories, or glob patterns. Takes an array of strings or just one - * string. - */ - unwatch(paths: string | ReadonlyArray): this - - /** - * Returns an object representing all the paths on the file system being watched by this - * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless - * the `cwd` option was used), and the values are arrays of the names of the items contained in - * each directory. - */ - getWatched(): { - [directory: string]: string[] - } - - /** - * Removes all listeners from watched files. - */ - close(): Promise - - on( - event: 'add' | 'addDir' | 'change', - listener: (path: string, stats?: fs.Stats) => void - ): this - - on( - event: 'all', - listener: ( - eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', - path: string, - stats?: fs.Stats - ) => void - ): this - - /** - * Error occurred - */ - on(event: 'error', listener: (error: Error) => void): this - - /** - * Exposes the native Node `fs.FSWatcher events` - */ - on( - event: 'raw', - listener: (eventName: string, path: string, details: any) => void - ): this - - /** - * Fires when the initial scan is complete - */ - on(event: 'ready', listener: () => void): this - - on(event: 'unlink' | 'unlinkDir', listener: (path: string) => void): this - - on(event: string, listener: (...args: any[]) => void): this -} - -export { FullReloadPayload } - -export { GeneralImportGlobOptions } - -export declare function getDepOptimizationConfig(config: ResolvedConfig, ssr: boolean): DepOptimizationConfig; - -export declare interface HmrContext { - file: string; - timestamp: number; - modules: Array; - read: () => string | Promise; - server: ViteDevServer; -} - -export declare interface HmrOptions { - protocol?: string; - host?: string; - port?: number; - clientPort?: number; - path?: string; - timeout?: number; - overlay?: boolean; - server?: Server; -} - -export { HMRPayload } - -export declare type HookHandler = T extends ObjectHook ? H : T; - -export declare interface HtmlTagDescriptor { - tag: string; - attrs?: Record; - children?: string | HtmlTagDescriptor[]; - /** - * default: 'head-prepend' - */ - injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend'; -} - -export declare namespace HttpProxy { - export type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed - - export type ProxyTargetUrl = string | Partial - - export interface ProxyTargetDetailed { - host: string - port: number - protocol?: string | undefined - hostname?: string | undefined - socketPath?: string | undefined - key?: string | undefined - passphrase?: string | undefined - pfx?: Buffer | string | undefined - cert?: string | undefined - ca?: string | undefined - ciphers?: string | undefined - secureProtocol?: string | undefined - } - - export type ErrorCallback = ( - err: Error, - req: http.IncomingMessage, - res: http.ServerResponse, - target?: ProxyTargetUrl - ) => void - - export class Server extends events.EventEmitter { - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - */ - constructor(options?: ServerOptions) - - /** - * Used for proxying regular HTTP(S) requests - * @param req - Client request. - * @param res - Client response. - * @param options - Additional options. - */ - web( - req: http.IncomingMessage, - res: http.ServerResponse, - options?: ServerOptions, - callback?: ErrorCallback - ): void - - /** - * Used for proxying regular HTTP(S) requests - * @param req - Client request. - * @param socket - Client socket. - * @param head - Client head. - * @param options - Additional options. - */ - ws( - req: http.IncomingMessage, - socket: unknown, - head: unknown, - options?: ServerOptions, - callback?: ErrorCallback - ): void - - /** - * A function that wraps the object in a webserver, for your convenience - * @param port - Port to listen on - */ - listen(port: number): Server - - /** - * A function that closes the inner webserver and stops listening on given port - */ - close(callback?: () => void): void - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createProxyServer(options?: ServerOptions): Server - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createServer(options?: ServerOptions): Server - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createProxy(options?: ServerOptions): Server - - addListener(event: string, listener: () => void): this - on(event: string, listener: () => void): this - on(event: 'error', listener: ErrorCallback): this - on( - event: 'start', - listener: ( - req: http.IncomingMessage, - res: http.ServerResponse, - target: ProxyTargetUrl - ) => void - ): this - on( - event: 'proxyReq', - listener: ( - proxyReq: http.ClientRequest, - req: http.IncomingMessage, - res: http.ServerResponse, - options: ServerOptions - ) => void - ): this - on( - event: 'proxyRes', - listener: ( - proxyRes: http.IncomingMessage, - req: http.IncomingMessage, - res: http.ServerResponse - ) => void - ): this - on( - event: 'proxyReqWs', - listener: ( - proxyReq: http.ClientRequest, - req: http.IncomingMessage, - socket: net.Socket, - options: ServerOptions, - head: any - ) => void - ): this - on( - event: 'econnreset', - listener: ( - err: Error, - req: http.IncomingMessage, - res: http.ServerResponse, - target: ProxyTargetUrl - ) => void - ): this - on( - event: 'end', - listener: ( - req: http.IncomingMessage, - res: http.ServerResponse, - proxyRes: http.IncomingMessage - ) => void - ): this - on( - event: 'close', - listener: ( - proxyRes: http.IncomingMessage, - proxySocket: net.Socket, - proxyHead: any - ) => void - ): this - - once(event: string, listener: () => void): this - removeListener(event: string, listener: () => void): this - removeAllListeners(event?: string): this - getMaxListeners(): number - setMaxListeners(n: number): this - listeners(event: string): Array<() => void> - emit(event: string, ...args: any[]): boolean - listenerCount(type: string): number - } - - export interface ServerOptions { - /** URL string to be parsed with the url module. */ - target?: ProxyTarget | undefined - /** URL string to be parsed with the url module. */ - forward?: ProxyTargetUrl | undefined - /** Object to be passed to http(s).request. */ - agent?: any - /** Object to be passed to https.createServer(). */ - ssl?: any - /** If you want to proxy websockets. */ - ws?: boolean | undefined - /** Adds x- forward headers. */ - xfwd?: boolean | undefined - /** Verify SSL certificate. */ - secure?: boolean | undefined - /** Explicitly specify if we are proxying to another proxy. */ - toProxy?: boolean | undefined - /** Specify whether you want to prepend the target's path to the proxy path. */ - prependPath?: boolean | undefined - /** Specify whether you want to ignore the proxy path of the incoming request. */ - ignorePath?: boolean | undefined - /** Local interface string to bind for outgoing connections. */ - localAddress?: string | undefined - /** Changes the origin of the host header to the target URL. */ - changeOrigin?: boolean | undefined - /** specify whether you want to keep letter case of response header key */ - preserveHeaderKeyCase?: boolean | undefined - /** Basic authentication i.e. 'user:password' to compute an Authorization header. */ - auth?: string | undefined - /** Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null. */ - hostRewrite?: string | undefined - /** Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false. */ - autoRewrite?: boolean | undefined - /** Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null. */ - protocolRewrite?: string | undefined - /** rewrites domain of set-cookie headers. */ - cookieDomainRewrite?: - | false - | string - | { [oldDomain: string]: string } - | undefined - /** rewrites path of set-cookie headers. Default: false */ - cookiePathRewrite?: - | false - | string - | { [oldPath: string]: string } - | undefined - /** object with extra headers to be added to target requests. */ - headers?: { [header: string]: string } | undefined - /** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */ - proxyTimeout?: number | undefined - /** Timeout (in milliseconds) for incoming requests */ - timeout?: number | undefined - /** Specify whether you want to follow redirects. Default: false */ - followRedirects?: boolean | undefined - /** If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event */ - selfHandleResponse?: boolean | undefined - /** Buffer */ - buffer?: stream.Stream | undefined - } -} - -export { ImportGlobEagerFunction } - -export { ImportGlobFunction } - -export { ImportGlobOptions } - -export declare type IndexHtmlTransform = IndexHtmlTransformHook | { - enforce?: 'pre' | 'post'; - transform: IndexHtmlTransformHook; -}; - -export declare interface IndexHtmlTransformContext { - /** - * public path when served - */ - path: string; - /** - * filename on disk - */ - filename: string; - server?: ViteDevServer; - bundle?: OutputBundle; - chunk?: OutputChunk; - originalUrl?: string; -} - -export declare type IndexHtmlTransformHook = (this: void, html: string, ctx: IndexHtmlTransformContext) => IndexHtmlTransformResult | void | Promise; - -export declare type IndexHtmlTransformResult = string | HtmlTagDescriptor[] | { - html: string; - tags: HtmlTagDescriptor[]; -}; - -export { InferCustomEventPayload } - -export declare interface InlineConfig extends UserConfig { - configFile?: string | false; - envFile?: false; -} - -export declare interface InternalResolveOptions extends Required { - root: string; - isBuild: boolean; - isProduction: boolean; - ssrConfig?: SSROptions; - packageCache?: PackageCache; - /** - * src code mode also attempts the following: - * - resolving /xxx as URLs - * - resolving bare imports from optimized deps - */ - asSrc?: boolean; - tryIndex?: boolean; - tryPrefix?: string; - skipPackageJson?: boolean; - preferRelative?: boolean; - isRequire?: boolean; - isFromTsImporter?: boolean; - tryEsmOnly?: boolean; - scan?: boolean; - ssrOptimizeCheck?: boolean; - getDepsOptimizer?: (ssr: boolean) => DepsOptimizer | undefined; - shouldExternalize?: (id: string) => boolean | undefined; - isHookNodeResolve?: boolean; -} - -export { InvalidatePayload } - -export declare function isDepsOptimizerEnabled(config: ResolvedConfig, ssr: boolean): boolean; - -export declare interface JsonOptions { - /** - * Generate a named export for every property of the JSON object - * @default true - */ - namedExports?: boolean; - /** - * Generate performant output as JSON.parse("stringified"). - * Enabling this will disable namedExports. - * @default false - */ - stringify?: boolean; -} - -export { KnownAsTypeMap } - -export declare interface LegacyOptions { - /** - * Revert vite build --ssr to the v2.9 strategy. Use CJS SSR build and v2.9 externalization heuristics - * - * @experimental - * @deprecated - * @default false - */ - buildSsrCjsExternalHeuristics?: boolean; -} - -export declare type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife'; - -export declare interface LibraryOptions { - /** - * Path of library entry - */ - entry: InputOption; - /** - * The name of the exposed global variable. Required when the `formats` option includes - * `umd` or `iife` - */ - name?: string; - /** - * Output bundle formats - * @default ['es', 'umd'] - */ - formats?: LibraryFormats[]; - /** - * The name of the package file output. The default file name is the name option - * of the project package.json. It can also be defined as a function taking the - * format as an argument. - */ - fileName?: string | ((format: ModuleFormat, entryName: string) => string); -} - -export declare function loadConfigFromFile(configEnv: ConfigEnv, configFile?: string, configRoot?: string, logLevel?: LogLevel): Promise<{ - path: string; - config: UserConfig; - dependencies: string[]; -} | null>; - -export declare function loadEnv(mode: string, envDir: string, prefixes?: string | string[]): Record; - -export declare interface LogErrorOptions extends LogOptions { - error?: Error | RollupError | null; -} - -export declare interface Logger { - info(msg: string, options?: LogOptions): void; - warn(msg: string, options?: LogOptions): void; - warnOnce(msg: string, options?: LogOptions): void; - error(msg: string, options?: LogErrorOptions): void; - clearScreen(type: LogType): void; - hasErrorLogged(error: Error | RollupError): boolean; - hasWarned: boolean; -} - -export declare interface LoggerOptions { - prefix?: string; - allowClearScreen?: boolean; - customLogger?: Logger; -} - -export declare type LogLevel = LogType | 'silent'; - -export declare interface LogOptions { - clear?: boolean; - timestamp?: boolean; -} - -export declare type LogType = 'error' | 'warn' | 'info'; - -export declare type Manifest = Record; - -export declare interface ManifestChunk { - src?: string; - file: string; - css?: string[]; - assets?: string[]; - isEntry?: boolean; - isDynamicEntry?: boolean; - imports?: string[]; - dynamicImports?: string[]; -} - -export declare type MapToFunction = T extends Function ? T : never - -export declare type Matcher = AnymatchPattern | AnymatchPattern[] - -export declare function mergeAlias(a?: AliasOptions, b?: AliasOptions): AliasOptions | undefined; - -export declare function mergeConfig(defaults: Record, overrides: Record, isRoot?: boolean): Record; - -export declare class ModuleGraph { - private resolveId; - urlToModuleMap: Map; - idToModuleMap: Map; - fileToModulesMap: Map>; - safeModulesPath: Set; - constructor(resolveId: (url: string, ssr: boolean) => Promise); - getModuleByUrl(rawUrl: string, ssr?: boolean): Promise; - getModuleById(id: string): ModuleNode | undefined; - getModulesByFile(file: string): Set | undefined; - onFileChange(file: string): void; - invalidateModule(mod: ModuleNode, seen?: Set, timestamp?: number): void; - invalidateAll(): void; - /** - * Update the module graph based on a module's updated imports information - * If there are dependencies that no longer have any importers, they are - * returned as a Set. - */ - updateModuleInfo(mod: ModuleNode, importedModules: Set, importedBindings: Map> | null, acceptedModules: Set, acceptedExports: Set | null, isSelfAccepting: boolean, ssr?: boolean): Promise | undefined>; - ensureEntryFromUrl(rawUrl: string, ssr?: boolean, setIsSelfAccepting?: boolean): Promise; - createFileOnlyEntry(file: string): ModuleNode; - resolveUrl(url: string, ssr?: boolean): Promise; -} - -export declare class ModuleNode { - /** - * Public served url path, starts with / - */ - url: string; - /** - * Resolved file system path + query - */ - id: string | null; - file: string | null; - type: 'js' | 'css'; - info?: ModuleInfo; - meta?: Record; - importers: Set; - importedModules: Set; - acceptedHmrDeps: Set; - acceptedHmrExports: Set | null; - importedBindings: Map> | null; - isSelfAccepting?: boolean; - transformResult: TransformResult | null; - ssrTransformResult: TransformResult | null; - ssrModule: Record | null; - ssrError: Error | null; - lastHMRTimestamp: number; - lastInvalidationTimestamp: number; - /** - * @param setIsSelfAccepting - set `false` to set `isSelfAccepting` later. e.g. #7870 - */ - constructor(url: string, setIsSelfAccepting?: boolean); -} - -export declare interface ModulePreloadOptions { - /** - * Whether to inject a module preload polyfill. - * Note: does not apply to library mode. - * @default true - */ - polyfill?: boolean; - /** - * Resolve the list of dependencies to preload for a given dynamic import - * @experimental - */ - resolveDependencies?: ResolveModulePreloadDependenciesFn; -} - -export declare function normalizePath(id: string): string; - -export declare interface OptimizedDepInfo { - id: string; - file: string; - src?: string; - needsInterop?: boolean; - browserHash?: string; - fileHash?: string; - /** - * During optimization, ids can still be resolved to their final location - * but the bundles may not yet be saved to disk - */ - processing?: Promise; - /** - * ExportData cache, discovered deps will parse the src entry to get exports - * data used both to define if interop is needed and when pre-bundling - */ - exportsData?: Promise; -} - -/** - * Scan and optimize dependencies within a project. - * Used by Vite CLI when running `vite optimize`. - */ -export declare function optimizeDeps(config: ResolvedConfig, force?: boolean | undefined, asCommand?: boolean): Promise; - -/** Cache for package.json resolution and package.json contents */ -export declare type PackageCache = Map; - -export declare interface PackageData { - dir: string; - hasSideEffects: (id: string) => boolean | 'no-treeshake'; - webResolvedImports: Record; - nodeResolvedImports: Record; - setResolvedCache: (key: string, entry: string, targetWeb: boolean) => void; - getResolvedCache: (key: string, targetWeb: boolean) => string | undefined; - data: { - [field: string]: any; - name: string; - type: string; - version: string; - main: string; - module: string; - browser: string | Record; - exports: string | Record | string[]; - dependencies: Record; - }; -} - -/** - * Vite plugins extends the Rollup plugin interface with a few extra - * vite-specific options. A valid vite plugin is also a valid Rollup plugin. - * On the contrary, a Rollup plugin may or may NOT be a valid vite universal - * plugin, since some Rollup features do not make sense in an unbundled - * dev server context. That said, as long as a rollup plugin doesn't have strong - * coupling between its bundle phase and output phase hooks then it should - * just work (that means, most of them). - * - * By default, the plugins are run during both serve and build. When a plugin - * is applied during serve, it will only run **non output plugin hooks** (see - * rollup type definition of {@link rollup#PluginHooks}). You can think of the - * dev server as only running `const bundle = rollup.rollup()` but never calling - * `bundle.generate()`. - * - * A plugin that expects to have different behavior depending on serve/build can - * export a factory function that receives the command being run via options. - * - * If a plugin should be applied only for server or build, a function format - * config file can be used to conditional determine the plugins to use. - */ -declare interface Plugin_2 extends Plugin_3 { - /** - * Enforce plugin invocation tier similar to webpack loaders. - * - * Plugin invocation order: - * - alias resolution - * - `enforce: 'pre'` plugins - * - vite core plugins - * - normal plugins - * - vite build plugins - * - `enforce: 'post'` plugins - * - vite build post plugins - */ - enforce?: 'pre' | 'post'; - /** - * Apply the plugin only for serve or build, or on certain conditions. - */ - apply?: 'serve' | 'build' | ((this: void, config: UserConfig, env: ConfigEnv) => boolean); - /** - * Modify vite config before it's resolved. The hook can either mutate the - * passed-in config directly, or return a partial config object that will be - * deeply merged into existing config. - * - * Note: User plugins are resolved before running this hook so injecting other - * plugins inside the `config` hook will have no effect. - */ - config?: ObjectHook<(this: void, config: UserConfig, env: ConfigEnv) => UserConfig | null | void | Promise>; - /** - * Use this hook to read and store the final resolved vite config. - */ - configResolved?: ObjectHook<(this: void, config: ResolvedConfig) => void | Promise>; - /** - * Configure the vite server. The hook receives the {@link ViteDevServer} - * instance. This can also be used to store a reference to the server - * for use in other hooks. - * - * The hooks will be called before internal middlewares are applied. A hook - * can return a post hook that will be called after internal middlewares - * are applied. Hook can be async functions and will be called in series. - */ - configureServer?: ObjectHook; - /** - * Configure the preview server. The hook receives the connect server and - * its underlying http server. - * - * The hooks are called before other middlewares are applied. A hook can - * return a post hook that will be called after other middlewares are - * applied. Hooks can be async functions and will be called in series. - */ - configurePreviewServer?: ObjectHook; - /** - * Transform index.html. - * The hook receives the following arguments: - * - * - html: string - * - ctx?: vite.ServerContext (only present during serve) - * - bundle?: rollup.OutputBundle (only present during build) - * - * It can either return a transformed string, or a list of html tag - * descriptors that will be injected into the `` or ``. - * - * By default the transform is applied **after** vite's internal html - * transform. If you need to apply the transform before vite, use an object: - * `{ enforce: 'pre', transform: hook }` - */ - transformIndexHtml?: IndexHtmlTransform; - /** - * Perform custom handling of HMR updates. - * The handler receives a context containing changed filename, timestamp, a - * list of modules affected by the file change, and the dev server instance. - * - * - The hook can return a filtered list of modules to narrow down the update. - * e.g. for a Vue SFC, we can narrow down the part to update by comparing - * the descriptors. - * - * - The hook can also return an empty array and then perform custom updates - * by sending a custom hmr payload via server.ws.send(). - * - * - If the hook doesn't return a value, the hmr update will be performed as - * normal. - */ - handleHotUpdate?: ObjectHook<(this: void, ctx: HmrContext) => Array | void | Promise | void>>; - /** - * extend hooks with ssr flag - */ - resolveId?: ObjectHook<(this: PluginContext, source: string, importer: string | undefined, options: { - custom?: CustomPluginOptions; - ssr?: boolean; - /* Excluded from this release type: scan */ - isEntry: boolean; - }) => Promise | ResolveIdResult>; - load?: ObjectHook<(this: PluginContext, id: string, options?: { - ssr?: boolean; - }) => Promise | LoadResult>; - transform?: ObjectHook<(this: TransformPluginContext, code: string, id: string, options?: { - ssr?: boolean; - }) => Promise | TransformResult_2>; -} -export { Plugin_2 as Plugin } - -export declare interface PluginContainer { - options: InputOptions; - getModuleInfo(id: string): ModuleInfo | null; - buildStart(options: InputOptions): Promise; - resolveId(id: string, importer?: string, options?: { - custom?: CustomPluginOptions; - skip?: Set; - ssr?: boolean; - /* Excluded from this release type: scan */ - isEntry?: boolean; - }): Promise; - transform(code: string, id: string, options?: { - inMap?: SourceDescription['map']; - ssr?: boolean; - }): Promise; - load(id: string, options?: { - ssr?: boolean; - }): Promise; - close(): Promise; -} - -export declare interface PluginHookUtils { - getSortedPlugins: (hookName: keyof Plugin_2) => Plugin_2[]; - getSortedPluginHooks: (hookName: K) => NonNullable>[]; -} - -export declare type PluginOption = Plugin_2 | false | null | undefined | PluginOption[] | Promise; - -/** - * @experimental - */ -export declare function preprocessCSS(code: string, filename: string, config: ResolvedConfig): Promise; - -export declare interface PreprocessCSSResult { - code: string; - map?: SourceMapInput; - modules?: Record; - deps?: Set; -} - -/** - * Starts the Vite server in preview mode, to simulate a production deployment - */ -export declare function preview(inlineConfig?: InlineConfig): Promise; - -export declare interface PreviewOptions extends CommonServerOptions { -} - -export declare interface PreviewServer { - /** - * The resolved vite config object - */ - config: ResolvedConfig; - /** - * native Node http server instance - */ - httpServer: http.Server; - /** - * The resolved urls Vite prints on the CLI - */ - resolvedUrls: ResolvedServerUrls; - /** - * Print server urls - */ - printUrls(): void; -} - -export declare type PreviewServerHook = (this: void, server: { - middlewares: Connect.Server; - httpServer: http.Server; -}) => (() => void) | void | Promise<(() => void) | void>; - -export declare interface ProxyOptions extends HttpProxy.ServerOptions { - /** - * rewrite path - */ - rewrite?: (path: string) => string; - /** - * configure the proxy server (e.g. listen to events) - */ - configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void; - /** - * webpack-dev-server style bypass function - */ - bypass?: (req: http.IncomingMessage, res: http.ServerResponse, options: ProxyOptions) => void | null | undefined | false | string; -} - -export { PrunePayload } - -export declare type RenderBuiltAssetUrl = (filename: string, type: { - type: 'asset' | 'public'; - hostId: string; - hostType: 'js' | 'css' | 'html'; - ssr: boolean; -}) => string | { - relative?: boolean; - runtime?: string; -} | undefined; - -/** - * Resolve base url. Note that some users use Vite to build for non-web targets like - * electron or expects to deploy - */ -export declare function resolveBaseUrl(base: string | undefined, isBuild: boolean, logger: Logger): string; - -export declare function resolveConfig(inlineConfig: InlineConfig, command: 'build' | 'serve', defaultMode?: string): Promise; - -export declare interface ResolvedBuildOptions extends Required> { - modulePreload: false | ResolvedModulePreloadOptions; -} - -export declare type ResolvedConfig = Readonly & { - configFile: string | undefined; - configFileDependencies: string[]; - inlineConfig: InlineConfig; - root: string; - base: string; - publicDir: string; - cacheDir: string; - command: 'build' | 'serve'; - mode: string; - isWorker: boolean; - /* Excluded from this release type: mainConfig */ - isProduction: boolean; - env: Record; - resolve: Required & { - alias: Alias[]; - }; - plugins: readonly Plugin_2[]; - server: ResolvedServerOptions; - build: ResolvedBuildOptions; - preview: ResolvedPreviewOptions; - ssr: ResolvedSSROptions; - assetsInclude: (file: string) => boolean; - logger: Logger; - createResolver: (options?: Partial) => ResolveFn; - optimizeDeps: DepOptimizationOptions; - /* Excluded from this release type: packageCache */ - worker: ResolveWorkerOptions; - appType: AppType; - experimental: ExperimentalOptions; -} & PluginHookUtils>; - -export declare interface ResolvedModulePreloadOptions { - polyfill: boolean; - resolveDependencies?: ResolveModulePreloadDependenciesFn; -} - -export declare interface ResolvedPreviewOptions extends PreviewOptions { -} - -export declare interface ResolvedServerOptions extends ServerOptions { - fs: Required; - middlewareMode: boolean; -} - -export declare interface ResolvedServerUrls { - local: string[]; - network: string[]; -} - -export declare interface ResolvedSSROptions extends SSROptions { - target: SSRTarget; - format: SSRFormat; - optimizeDeps: SsrDepOptimizationOptions; -} - -export declare type ResolvedUrl = [ -url: string, -resolvedId: string, -meta: object | null | undefined -]; - -export declare function resolveEnvPrefix({ envPrefix }: UserConfig): string[]; - -export declare type ResolveFn = (id: string, importer?: string, aliasOnly?: boolean, ssr?: boolean) => Promise; - -export declare type ResolveModulePreloadDependenciesFn = (filename: string, deps: string[], context: { - hostId: string; - hostType: 'html' | 'js'; -}) => string[]; - -export declare interface ResolveOptions { - mainFields?: string[]; - /** - * @deprecated In future, `mainFields` should be used instead. - * @default true - */ - browserField?: boolean; - conditions?: string[]; - extensions?: string[]; - dedupe?: string[]; - preserveSymlinks?: boolean; -} - -export declare function resolvePackageData(id: string, basedir: string, preserveSymlinks?: boolean, packageCache?: PackageCache): PackageData | null; - -export declare function resolvePackageEntry(id: string, { dir, data, setResolvedCache, getResolvedCache }: PackageData, targetWeb: boolean, options: InternalResolveOptions): string | undefined; - -export declare type ResolverFunction = MapToFunction - -export declare interface ResolverObject { - buildStart?: PluginHooks['buildStart'] - resolveId: ResolverFunction -} - -export declare interface ResolveWorkerOptions extends PluginHookUtils { - format: 'es' | 'iife'; - plugins: Plugin_2[]; - rollupOptions: RollupOptions; -} - -/** - * https://github.com/rollup/plugins/blob/master/packages/commonjs/types/index.d.ts - * - * This source code is licensed under the MIT license found in the - * LICENSE file at - * https://github.com/rollup/plugins/blob/master/LICENSE - */ -export declare interface RollupCommonJSOptions { - /** - * A minimatch pattern, or array of patterns, which specifies the files in - * the build the plugin should operate on. By default, all files with - * extension `".cjs"` or those in `extensions` are included, but you can - * narrow this list by only including specific files. These files will be - * analyzed and transpiled if either the analysis does not find ES module - * specific statements or `transformMixedEsModules` is `true`. - * @default undefined - */ - include?: string | RegExp | readonly (string | RegExp)[] - /** - * A minimatch pattern, or array of patterns, which specifies the files in - * the build the plugin should _ignore_. By default, all files with - * extensions other than those in `extensions` or `".cjs"` are ignored, but you - * can exclude additional files. See also the `include` option. - * @default undefined - */ - exclude?: string | RegExp | readonly (string | RegExp)[] - /** - * For extensionless imports, search for extensions other than .js in the - * order specified. Note that you need to make sure that non-JavaScript files - * are transpiled by another plugin first. - * @default [ '.js' ] - */ - extensions?: ReadonlyArray - /** - * If true then uses of `global` won't be dealt with by this plugin - * @default false - */ - ignoreGlobal?: boolean - /** - * If false, skips source map generation for CommonJS modules. This will - * improve performance. - * @default true - */ - sourceMap?: boolean - /** - * Some `require` calls cannot be resolved statically to be translated to - * imports. - * When this option is set to `false`, the generated code will either - * directly throw an error when such a call is encountered or, when - * `dynamicRequireTargets` is used, when such a call cannot be resolved with a - * configured dynamic require target. - * Setting this option to `true` will instead leave the `require` call in the - * code or use it as a fallback for `dynamicRequireTargets`. - * @default false - */ - ignoreDynamicRequires?: boolean - /** - * Instructs the plugin whether to enable mixed module transformations. This - * is useful in scenarios with modules that contain a mix of ES `import` - * statements and CommonJS `require` expressions. Set to `true` if `require` - * calls should be transformed to imports in mixed modules, or `false` if the - * `require` expressions should survive the transformation. The latter can be - * important if the code contains environment detection, or you are coding - * for an environment with special treatment for `require` calls such as - * ElectronJS. See also the `ignore` option. - * @default false - */ - transformMixedEsModules?: boolean - /** - * By default, this plugin will try to hoist `require` statements as imports - * to the top of each file. While this works well for many code bases and - * allows for very efficient ESM output, it does not perfectly capture - * CommonJS semantics as the order of side effects like log statements may - * change. But it is especially problematic when there are circular `require` - * calls between CommonJS modules as those often rely on the lazy execution of - * nested `require` calls. - * - * Setting this option to `true` will wrap all CommonJS files in functions - * which are executed when they are required for the first time, preserving - * NodeJS semantics. Note that this can have an impact on the size and - * performance of the generated code. - * - * The default value of `"auto"` will only wrap CommonJS files when they are - * part of a CommonJS dependency cycle, e.g. an index file that is required by - * many of its dependencies. All other CommonJS files are hoisted. This is the - * recommended setting for most code bases. - * - * `false` will entirely prevent wrapping and hoist all files. This may still - * work depending on the nature of cyclic dependencies but will often cause - * problems. - * - * You can also provide a minimatch pattern, or array of patterns, to only - * specify a subset of files which should be wrapped in functions for proper - * `require` semantics. - * - * `"debug"` works like `"auto"` but after bundling, it will display a warning - * containing a list of ids that have been wrapped which can be used as - * minimatch pattern for fine-tuning. - * @default "auto" - */ - strictRequires?: boolean | string | RegExp | readonly (string | RegExp)[] - /** - * Sometimes you have to leave require statements unconverted. Pass an array - * containing the IDs or a `id => boolean` function. - * @default [] - */ - ignore?: ReadonlyArray | ((id: string) => boolean) - /** - * In most cases, where `require` calls are inside a `try-catch` clause, - * they should be left unconverted as it requires an optional dependency - * that may or may not be installed beside the rolled up package. - * Due to the conversion of `require` to a static `import` - the call is - * hoisted to the top of the file, outside of the `try-catch` clause. - * - * - `true`: All `require` calls inside a `try` will be left unconverted. - * - `false`: All `require` calls inside a `try` will be converted as if the - * `try-catch` clause is not there. - * - `remove`: Remove all `require` calls from inside any `try` block. - * - `string[]`: Pass an array containing the IDs to left unconverted. - * - `((id: string) => boolean|'remove')`: Pass a function that control - * individual IDs. - * - * @default false - */ - ignoreTryCatch?: - | boolean - | 'remove' - | ReadonlyArray - | ((id: string) => boolean | 'remove') - /** - * Controls how to render imports from external dependencies. By default, - * this plugin assumes that all external dependencies are CommonJS. This - * means they are rendered as default imports to be compatible with e.g. - * NodeJS where ES modules can only import a default export from a CommonJS - * dependency. - * - * If you set `esmExternals` to `true`, this plugins assumes that all - * external dependencies are ES modules and respect the - * `requireReturnsDefault` option. If that option is not set, they will be - * rendered as namespace imports. - * - * You can also supply an array of ids to be treated as ES modules, or a - * function that will be passed each external id to determine if it is an ES - * module. - * @default false - */ - esmExternals?: boolean | ReadonlyArray | ((id: string) => boolean) - /** - * Controls what is returned when requiring an ES module from a CommonJS file. - * When using the `esmExternals` option, this will also apply to external - * modules. By default, this plugin will render those imports as namespace - * imports i.e. - * - * ```js - * // input - * const foo = require('foo'); - * - * // output - * import * as foo from 'foo'; - * ``` - * - * However there are some situations where this may not be desired. - * For these situations, you can change Rollup's behaviour either globally or - * per module. To change it globally, set the `requireReturnsDefault` option - * to one of the following values: - * - * - `false`: This is the default, requiring an ES module returns its - * namespace. This is the only option that will also add a marker - * `__esModule: true` to the namespace to support interop patterns in - * CommonJS modules that are transpiled ES modules. - * - `"namespace"`: Like `false`, requiring an ES module returns its - * namespace, but the plugin does not add the `__esModule` marker and thus - * creates more efficient code. For external dependencies when using - * `esmExternals: true`, no additional interop code is generated. - * - `"auto"`: This is complementary to how `output.exports: "auto"` works in - * Rollup: If a module has a default export and no named exports, requiring - * that module returns the default export. In all other cases, the namespace - * is returned. For external dependencies when using `esmExternals: true`, a - * corresponding interop helper is added. - * - `"preferred"`: If a module has a default export, requiring that module - * always returns the default export, no matter whether additional named - * exports exist. This is similar to how previous versions of this plugin - * worked. Again for external dependencies when using `esmExternals: true`, - * an interop helper is added. - * - `true`: This will always try to return the default export on require - * without checking if it actually exists. This can throw at build time if - * there is no default export. This is how external dependencies are handled - * when `esmExternals` is not used. The advantage over the other options is - * that, like `false`, this does not add an interop helper for external - * dependencies, keeping the code lean. - * - * To change this for individual modules, you can supply a function for - * `requireReturnsDefault` instead. This function will then be called once for - * each required ES module or external dependency with the corresponding id - * and allows you to return different values for different modules. - * @default false - */ - requireReturnsDefault?: - | boolean - | 'auto' - | 'preferred' - | 'namespace' - | ((id: string) => boolean | 'auto' | 'preferred' | 'namespace') - - /** - * @default "auto" - */ - defaultIsModuleExports?: boolean | 'auto' | ((id: string) => boolean | 'auto') - /** - * Some modules contain dynamic `require` calls, or require modules that - * contain circular dependencies, which are not handled well by static - * imports. Including those modules as `dynamicRequireTargets` will simulate a - * CommonJS (NodeJS-like) environment for them with support for dynamic - * dependencies. It also enables `strictRequires` for those modules. - * - * Note: In extreme cases, this feature may result in some paths being - * rendered as absolute in the final bundle. The plugin tries to avoid - * exposing paths from the local machine, but if you are `dynamicRequirePaths` - * with paths that are far away from your project's folder, that may require - * replacing strings like `"/Users/John/Desktop/foo-project/"` -\> `"/"`. - */ - dynamicRequireTargets?: string | ReadonlyArray - /** - * To avoid long paths when using the `dynamicRequireTargets` option, you can use this option to specify a directory - * that is a common parent for all files that use dynamic require statements. Using a directory higher up such as `/` - * may lead to unnecessarily long paths in the generated code and may expose directory names on your machine like your - * home directory name. By default it uses the current working directory. - */ - dynamicRequireRoot?: string -} - -export declare interface RollupDynamicImportVarsOptions { - /** - * Files to include in this plugin (default all). - * @default [] - */ - include?: string | RegExp | (string | RegExp)[] - /** - * Files to exclude in this plugin (default none). - * @default [] - */ - exclude?: string | RegExp | (string | RegExp)[] - /** - * By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched. - * @default false - */ - warnOnError?: boolean -} - -export { rollupVersion } - -/** - * Search up for the nearest workspace root - */ -export declare function searchForWorkspaceRoot(current: string, root?: string): string; - -export declare function send(req: IncomingMessage, res: ServerResponse, content: string | Buffer, type: string, options: SendOptions): void; - -export declare interface SendOptions { - etag?: string; - cacheControl?: string; - headers?: OutgoingHttpHeaders; - map?: SourceMap | null; -} - -export declare type ServerHook = (this: void, server: ViteDevServer) => (() => void) | void | Promise<(() => void) | void>; - -export declare interface ServerOptions extends CommonServerOptions { - /** - * Configure HMR-specific options (port, host, path & protocol) - */ - hmr?: HmrOptions | boolean; - /** - * chokidar watch options - * https://github.com/paulmillr/chokidar#api - */ - watch?: WatchOptions; - /** - * Create Vite dev server to be used as a middleware in an existing server - */ - middlewareMode?: boolean | 'html' | 'ssr'; - /** - * Prepend this folder to http requests, for use when proxying vite as a subfolder - * Should start and end with the `/` character - */ - base?: string; - /** - * Options for files served via '/\@fs/'. - */ - fs?: FileSystemServeOptions; - /** - * Origin for the generated asset URLs. - * - * @example `http://127.0.0.1:8080` - */ - origin?: string; - /** - * Pre-transform known direct imports - * @default true - */ - preTransformRequests?: boolean; - /** - * Force dep pre-optimization regardless of whether deps have changed. - * - * @deprecated Use optimizeDeps.force instead, this option may be removed - * in a future minor version without following semver - */ - force?: boolean; -} - -export declare function sortUserPlugins(plugins: (Plugin_2 | Plugin_2[])[] | undefined): [Plugin_2[], Plugin_2[], Plugin_2[]]; - -export declare function splitVendorChunk(options?: { - cache?: SplitVendorChunkCache; -}): GetManualChunk; - -export declare class SplitVendorChunkCache { - cache: Map; - constructor(); - reset(): void; -} - -export declare function splitVendorChunkPlugin(): Plugin_2; - -export declare type SsrDepOptimizationOptions = DepOptimizationConfig; - -export declare type SSRFormat = 'esm' | 'cjs'; - -export declare interface SSROptions { - noExternal?: string | RegExp | (string | RegExp)[] | true; - external?: string[]; - /** - * Define the target for the ssr build. The browser field in package.json - * is ignored for node but used if webworker is the target - * Default: 'node' - */ - target?: SSRTarget; - /** - * Define the format for the ssr build. Since Vite v3 the SSR build generates ESM by default. - * `'cjs'` can be selected to generate a CJS build, but it isn't recommended. This option is - * left marked as experimental to give users more time to update to ESM. CJS builds requires - * complex externalization heuristics that aren't present in the ESM format. - * @experimental - */ - format?: SSRFormat; - /** - * Control over which dependencies are optimized during SSR and esbuild options - * During build: - * no external CJS dependencies are optimized by default - * During dev: - * explicit no external CJS dependencies are optimized by default - * @experimental - */ - optimizeDeps?: SsrDepOptimizationOptions; -} - -export declare type SSRTarget = 'node' | 'webworker'; - -export declare namespace Terser { - export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 - - export interface ParseOptions { - bare_returns?: boolean - /** @deprecated legacy option. Currently, all supported EcmaScript is valid to parse. */ - ecma?: ECMA - html5_comments?: boolean - shebang?: boolean - } - - export interface CompressOptions { - arguments?: boolean - arrows?: boolean - booleans_as_integers?: boolean - booleans?: boolean - collapse_vars?: boolean - comparisons?: boolean - computed_props?: boolean - conditionals?: boolean - dead_code?: boolean - defaults?: boolean - directives?: boolean - drop_console?: boolean - drop_debugger?: boolean - ecma?: ECMA - evaluate?: boolean - expression?: boolean - global_defs?: object - hoist_funs?: boolean - hoist_props?: boolean - hoist_vars?: boolean - ie8?: boolean - if_return?: boolean - inline?: boolean | InlineFunctions - join_vars?: boolean - keep_classnames?: boolean | RegExp - keep_fargs?: boolean - keep_fnames?: boolean | RegExp - keep_infinity?: boolean - loops?: boolean - module?: boolean - negate_iife?: boolean - passes?: number - properties?: boolean - pure_funcs?: string[] - pure_getters?: boolean | 'strict' - reduce_funcs?: boolean - reduce_vars?: boolean - sequences?: boolean | number - side_effects?: boolean - switches?: boolean - toplevel?: boolean - top_retain?: null | string | string[] | RegExp - typeofs?: boolean - unsafe_arrows?: boolean - unsafe?: boolean - unsafe_comps?: boolean - unsafe_Function?: boolean - unsafe_math?: boolean - unsafe_symbols?: boolean - unsafe_methods?: boolean - unsafe_proto?: boolean - unsafe_regexp?: boolean - unsafe_undefined?: boolean - unused?: boolean - } - - export enum InlineFunctions { - Disabled = 0, - SimpleFunctions = 1, - WithArguments = 2, - WithArgumentsAndVariables = 3 - } - - export interface MangleOptions { - eval?: boolean - keep_classnames?: boolean | RegExp - keep_fnames?: boolean | RegExp - module?: boolean - nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler - properties?: boolean | ManglePropertiesOptions - reserved?: string[] - safari10?: boolean - toplevel?: boolean - } - - /** - * An identifier mangler for which the output is invariant with respect to the source code. - */ - export interface SimpleIdentifierMangler { - /** - * Obtains the nth most favored (usually shortest) identifier to rename a variable to. - * The mangler will increment n and retry until the return value is not in use in scope, and is not a reserved word. - * This function is expected to be stable; Evaluating get(n) === get(n) should always return true. - * @param n - The ordinal of the identifier. - */ - get(n: number): string - } - - /** - * An identifier mangler that leverages character frequency analysis to determine identifier precedence. - */ - export interface WeightedIdentifierMangler extends SimpleIdentifierMangler { - /** - * Modifies the internal weighting of the input characters by the specified delta. - * Will be invoked on the entire printed AST, and then deduct mangleable identifiers. - * @param chars - The characters to modify the weighting of. - * @param delta - The numeric weight to add to the characters. - */ - consider(chars: string, delta: number): number - /** - * Resets character weights. - */ - reset(): void - /** - * Sorts identifiers by character frequency, in preparation for calls to get(n). - */ - sort(): void - } - - export interface ManglePropertiesOptions { - builtins?: boolean - debug?: boolean - keep_quoted?: boolean | 'strict' - nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler - regex?: RegExp | string - reserved?: string[] - } - - export interface FormatOptions { - ascii_only?: boolean - /** @deprecated Not implemented anymore */ - beautify?: boolean - braces?: boolean - comments?: - | boolean - | 'all' - | 'some' - | RegExp - | (( - node: any, - comment: { - value: string - type: 'comment1' | 'comment2' | 'comment3' | 'comment4' - pos: number - line: number - col: number - } - ) => boolean) - ecma?: ECMA - ie8?: boolean - keep_numbers?: boolean - indent_level?: number - indent_start?: number - inline_script?: boolean - keep_quoted_props?: boolean - max_line_len?: number | false - preamble?: string - preserve_annotations?: boolean - quote_keys?: boolean - quote_style?: OutputQuoteStyle - safari10?: boolean - semicolons?: boolean - shebang?: boolean - shorthand?: boolean - source_map?: SourceMapOptions - webkit?: boolean - width?: number - wrap_iife?: boolean - wrap_func_args?: boolean - } - - export enum OutputQuoteStyle { - PreferDouble = 0, - AlwaysSingle = 1, - AlwaysDouble = 2, - AlwaysOriginal = 3 - } - - export interface MinifyOptions { - compress?: boolean | CompressOptions - ecma?: ECMA - enclose?: boolean | string - ie8?: boolean - keep_classnames?: boolean | RegExp - keep_fnames?: boolean | RegExp - mangle?: boolean | MangleOptions - module?: boolean - nameCache?: object - format?: FormatOptions - /** @deprecated deprecated */ - output?: FormatOptions - parse?: ParseOptions - safari10?: boolean - sourceMap?: boolean | SourceMapOptions - toplevel?: boolean - } - - export interface MinifyOutput { - code?: string - map?: object | string - decoded_map?: object | null - } - - export interface SourceMapOptions { - /** Source map object, 'inline' or source map file content */ - content?: object | string - includeSources?: boolean - filename?: string - root?: string - url?: string | 'inline' - } -} - -export declare interface TransformOptions { - ssr?: boolean; - html?: boolean; -} - -export declare interface TransformResult { - code: string; - map: SourceMap | null; - etag?: string; - deps?: string[]; - dynamicDeps?: string[]; -} - -export declare function transformWithEsbuild(code: string, filename: string, options?: EsbuildTransformOptions, inMap?: object): Promise; - -export { Update } - -export { UpdatePayload } - -export declare interface UserConfig { - /** - * Project root directory. Can be an absolute path, or a path relative from - * the location of the config file itself. - * @default process.cwd() - */ - root?: string; - /** - * Base public path when served in development or production. - * @default '/' - */ - base?: string; - /** - * Directory to serve as plain static assets. Files in this directory are - * served and copied to build dist dir as-is without transform. The value - * can be either an absolute file system path or a path relative to project root. - * - * Set to `false` or an empty string to disable copied static assets to build dist dir. - * @default 'public' - */ - publicDir?: string | false; - /** - * Directory to save cache files. Files in this directory are pre-bundled - * deps or some other cache files that generated by vite, which can improve - * the performance. You can use `--force` flag or manually delete the directory - * to regenerate the cache files. The value can be either an absolute file - * system path or a path relative to project root. - * Default to `.vite` when no `package.json` is detected. - * @default 'node_modules/.vite' - */ - cacheDir?: string; - /** - * Explicitly set a mode to run in. This will override the default mode for - * each command, and can be overridden by the command line --mode option. - */ - mode?: string; - /** - * Define global variable replacements. - * Entries will be defined on `window` during dev and replaced during build. - */ - define?: Record; - /** - * Array of vite plugins to use. - */ - plugins?: PluginOption[]; - /** - * Configure resolver - */ - resolve?: ResolveOptions & { - alias?: AliasOptions; - }; - /** - * CSS related options (preprocessors and CSS modules) - */ - css?: CSSOptions; - /** - * JSON loading options - */ - json?: JsonOptions; - /** - * Transform options to pass to esbuild. - * Or set to `false` to disable esbuild. - */ - esbuild?: ESBuildOptions | false; - /** - * Specify additional picomatch patterns to be treated as static assets. - */ - assetsInclude?: string | RegExp | (string | RegExp)[]; - /** - * Server specific options, e.g. host, port, https... - */ - server?: ServerOptions; - /** - * Build specific options - */ - build?: BuildOptions; - /** - * Preview specific options, e.g. host, port, https... - */ - preview?: PreviewOptions; - /** - * Dep optimization options - */ - optimizeDeps?: DepOptimizationOptions; - /** - * SSR specific options - */ - ssr?: SSROptions; - /** - * Experimental features - * - * Features under this field could change in the future and might NOT follow semver. - * Please be careful and always pin Vite's version when using them. - * @experimental - */ - experimental?: ExperimentalOptions; - /** - * Legacy options - * - * Features under this field only follow semver for patches, they could be removed in a - * future minor version. Please always pin Vite's version to a minor when using them. - */ - legacy?: LegacyOptions; - /** - * Log level. - * Default: 'info' - */ - logLevel?: LogLevel; - /** - * Custom logger. - */ - customLogger?: Logger; - /** - * Default: true - */ - clearScreen?: boolean; - /** - * Environment files directory. Can be an absolute path, or a path relative from - * the location of the config file itself. - * @default root - */ - envDir?: string; - /** - * Env variables starts with `envPrefix` will be exposed to your client source code via import.meta.env. - * @default 'VITE_' - */ - envPrefix?: string | string[]; - /** - * Worker bundle options - */ - worker?: { - /** - * Output format for worker bundle - * @default 'iife' - */ - format?: 'es' | 'iife'; - /** - * Vite plugins that apply to worker bundle - */ - plugins?: PluginOption[]; - /** - * Rollup options to build worker bundle - */ - rollupOptions?: Omit; - }; - /** - * Whether your application is a Single Page Application (SPA), - * a Multi-Page Application (MPA), or Custom Application (SSR - * and frameworks with custom HTML handling) - * @default 'spa' - */ - appType?: AppType; -} - -export declare type UserConfigExport = UserConfig | Promise | UserConfigFn; - -export declare type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise; - -export declare const version: string; - -export declare interface ViteDevServer { - /** - * The resolved vite config object - */ - config: ResolvedConfig; - /** - * A connect app instance. - * - Can be used to attach custom middlewares to the dev server. - * - Can also be used as the handler function of a custom http server - * or as a middleware in any connect-style Node.js frameworks - * - * https://github.com/senchalabs/connect#use-middleware - */ - middlewares: Connect.Server; - /** - * native Node http server instance - * will be null in middleware mode - */ - httpServer: http.Server | null; - /** - * chokidar watcher instance - * https://github.com/paulmillr/chokidar#api - */ - watcher: FSWatcher; - /** - * web socket server with `send(payload)` method - */ - ws: WebSocketServer; - /** - * Rollup plugin container that can run plugin hooks on a given file - */ - pluginContainer: PluginContainer; - /** - * Module graph that tracks the import relationships, url to file mapping - * and hmr state. - */ - moduleGraph: ModuleGraph; - /** - * The resolved urls Vite prints on the CLI. null in middleware mode or - * before `server.listen` is called. - */ - resolvedUrls: ResolvedServerUrls | null; - /** - * Programmatically resolve, load and transform a URL and get the result - * without going through the http request pipeline. - */ - transformRequest(url: string, options?: TransformOptions): Promise; - /** - * Apply vite built-in HTML transforms and any plugin HTML transforms. - */ - transformIndexHtml(url: string, html: string, originalUrl?: string): Promise; - /** - * Transform module code into SSR format. - */ - ssrTransform(code: string, inMap: SourceMap | null, url: string, originalCode?: string): Promise; - /** - * Load a given URL as an instantiated module for SSR. - */ - ssrLoadModule(url: string, opts?: { - fixStacktrace?: boolean; - }): Promise>; - /** - * Returns a fixed version of the given stack - */ - ssrRewriteStacktrace(stack: string): string; - /** - * Mutates the given SSR error by rewriting the stacktrace - */ - ssrFixStacktrace(e: Error): void; - /** - * Triggers HMR for a module in the module graph. You can use the `server.moduleGraph` - * API to retrieve the module to be reloaded. If `hmr` is false, this is a no-op. - */ - reloadModule(module: ModuleNode): Promise; - /** - * Start the server. - */ - listen(port?: number, isRestart?: boolean): Promise; - /** - * Stop the server. - */ - close(): Promise; - /** - * Print server urls - */ - printUrls(): void; - /** - * Restart the server. - * - * @param forceOptimize - force the optimizer to re-bundle, same as --force cli flag - */ - restart(forceOptimize?: boolean): Promise; - /* Excluded from this release type: _importGlobMap */ - /* Excluded from this release type: _ssrExternals */ - /* Excluded from this release type: _restartPromise */ - /* Excluded from this release type: _forceOptimizeOnRestart */ - /* Excluded from this release type: _pendingRequests */ - /* Excluded from this release type: _fsDenyGlob */ -} - -export declare interface WatchOptions { - /** - * Indicates whether the process should continue to run as long as files are being watched. If - * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, - * even if the process continues to run. - */ - persistent?: boolean - - /** - * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to - * be ignored. The whole relative or absolute path is tested, not just filename. If a function - * with two arguments is provided, it gets called twice per path - once with a single argument - * (the path), second time with two arguments (the path and the - * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). - */ - ignored?: Matcher - - /** - * If set to `false` then `add`/`addDir` events are also emitted for matching paths while - * instantiating the watching as chokidar discovers these file paths (before the `ready` event). - */ - ignoreInitial?: boolean - - /** - * When `false`, only the symlinks themselves will be watched for changes instead of following - * the link references and bubbling events through the link's path. - */ - followSymlinks?: boolean - - /** - * The base directory from which watch `paths` are to be derived. Paths emitted with events will - * be relative to this. - */ - cwd?: string - - /** - * If set to true then the strings passed to .watch() and .add() are treated as literal path - * names, even if they look like globs. - * - * @default false - */ - disableGlobbing?: boolean - - /** - * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU - * utilization, consider setting this to `false`. It is typically necessary to **set this to - * `true` to successfully watch files over a network**, and it may be necessary to successfully - * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides - * the `useFsEvents` default. - */ - usePolling?: boolean - - /** - * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly - * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on - * OS X, `usePolling: true` becomes the default. - */ - useFsEvents?: boolean - - /** - * If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that - * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is - * provided even in cases where it wasn't already available from the underlying watch events. - */ - alwaysStat?: boolean - - /** - * If set, limits how many levels of subdirectories will be traversed. - */ - depth?: number - - /** - * Interval of file system polling. - */ - interval?: number - - /** - * Interval of file system polling for binary files. ([see list of binary extensions](https://gi - * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) - */ - binaryInterval?: number - - /** - * Indicates whether to watch files that don't have read permissions if possible. If watching - * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed - * silently. - */ - ignorePermissionErrors?: boolean - - /** - * `true` if `useFsEvents` and `usePolling` are `false`. Automatically filters out artifacts - * that occur when using editors that use "atomic writes" instead of writing directly to the - * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` - * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, - * you can override it by setting `atomic` to a custom value, in milliseconds. - */ - atomic?: boolean | number - - /** - * can be set to an object in order to adjust timing params: - */ - awaitWriteFinish?: AwaitWriteFinishOptions | boolean -} - -declare class WebSocket_2 extends EventEmitter { - /** The connection is not yet open. */ - static readonly CONNECTING: 0 - /** The connection is open and ready to communicate. */ - static readonly OPEN: 1 - /** The connection is in the process of closing. */ - static readonly CLOSING: 2 - /** The connection is closed. */ - static readonly CLOSED: 3 - - binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' - readonly bufferedAmount: number - readonly extensions: string - /** Indicates whether the websocket is paused */ - readonly isPaused: boolean - readonly protocol: string - /** The current state of the connection */ - readonly readyState: - | typeof WebSocket_2.CONNECTING - | typeof WebSocket_2.OPEN - | typeof WebSocket_2.CLOSING - | typeof WebSocket_2.CLOSED - readonly url: string - - /** The connection is not yet open. */ - readonly CONNECTING: 0 - /** The connection is open and ready to communicate. */ - readonly OPEN: 1 - /** The connection is in the process of closing. */ - readonly CLOSING: 2 - /** The connection is closed. */ - readonly CLOSED: 3 - - onopen: ((event: WebSocket_2.Event) => void) | null - onerror: ((event: WebSocket_2.ErrorEvent) => void) | null - onclose: ((event: WebSocket_2.CloseEvent) => void) | null - onmessage: ((event: WebSocket_2.MessageEvent) => void) | null - - constructor(address: null) - constructor( - address: string | URL_2, - options?: WebSocket_2.ClientOptions | ClientRequestArgs - ) - constructor( - address: string | URL_2, - protocols?: string | string[], - options?: WebSocket_2.ClientOptions | ClientRequestArgs - ) - - close(code?: number, data?: string | Buffer): void - ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void - pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void - send(data: any, cb?: (err?: Error) => void): void - send( - data: any, - options: { - mask?: boolean | undefined - binary?: boolean | undefined - compress?: boolean | undefined - fin?: boolean | undefined - }, - cb?: (err?: Error) => void - ): void - terminate(): void - - /** - * Pause the websocket causing it to stop emitting events. Some events can still be - * emitted after this is called, until all buffered data is consumed. This method - * is a noop if the ready state is `CONNECTING` or `CLOSED`. - */ - pause(): void - /** - * Make a paused socket resume emitting events. This method is a noop if the ready - * state is `CONNECTING` or `CLOSED`. - */ - resume(): void - - // HTML5 WebSocket events - addEventListener( - method: 'message', - cb: (event: WebSocket_2.MessageEvent) => void, - options?: WebSocket_2.EventListenerOptions - ): void - addEventListener( - method: 'close', - cb: (event: WebSocket_2.CloseEvent) => void, - options?: WebSocket_2.EventListenerOptions - ): void - addEventListener( - method: 'error', - cb: (event: WebSocket_2.ErrorEvent) => void, - options?: WebSocket_2.EventListenerOptions - ): void - addEventListener( - method: 'open', - cb: (event: WebSocket_2.Event) => void, - options?: WebSocket_2.EventListenerOptions - ): void - - removeEventListener( - method: 'message', - cb: (event: WebSocket_2.MessageEvent) => void - ): void - removeEventListener( - method: 'close', - cb: (event: WebSocket_2.CloseEvent) => void - ): void - removeEventListener( - method: 'error', - cb: (event: WebSocket_2.ErrorEvent) => void - ): void - removeEventListener( - method: 'open', - cb: (event: WebSocket_2.Event) => void - ): void - - // Events - on( - event: 'close', - listener: (this: WebSocket_2, code: number, reason: Buffer) => void - ): this - on(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this - on( - event: 'upgrade', - listener: (this: WebSocket_2, request: IncomingMessage) => void - ): this - on( - event: 'message', - listener: ( - this: WebSocket_2, - data: WebSocket_2.RawData, - isBinary: boolean - ) => void - ): this - on(event: 'open', listener: (this: WebSocket_2) => void): this - on( - event: 'ping' | 'pong', - listener: (this: WebSocket_2, data: Buffer) => void - ): this - on( - event: 'unexpected-response', - listener: ( - this: WebSocket_2, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - on( - event: string | symbol, - listener: (this: WebSocket_2, ...args: any[]) => void - ): this - - once( - event: 'close', - listener: (this: WebSocket_2, code: number, reason: Buffer) => void - ): this - once(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this - once( - event: 'upgrade', - listener: (this: WebSocket_2, request: IncomingMessage) => void - ): this - once( - event: 'message', - listener: ( - this: WebSocket_2, - data: WebSocket_2.RawData, - isBinary: boolean - ) => void - ): this - once(event: 'open', listener: (this: WebSocket_2) => void): this - once( - event: 'ping' | 'pong', - listener: (this: WebSocket_2, data: Buffer) => void - ): this - once( - event: 'unexpected-response', - listener: ( - this: WebSocket_2, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - once( - event: string | symbol, - listener: (this: WebSocket_2, ...args: any[]) => void - ): this - - off( - event: 'close', - listener: (this: WebSocket_2, code: number, reason: Buffer) => void - ): this - off(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this - off( - event: 'upgrade', - listener: (this: WebSocket_2, request: IncomingMessage) => void - ): this - off( - event: 'message', - listener: ( - this: WebSocket_2, - data: WebSocket_2.RawData, - isBinary: boolean - ) => void - ): this - off(event: 'open', listener: (this: WebSocket_2) => void): this - off( - event: 'ping' | 'pong', - listener: (this: WebSocket_2, data: Buffer) => void - ): this - off( - event: 'unexpected-response', - listener: ( - this: WebSocket_2, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - off( - event: string | symbol, - listener: (this: WebSocket_2, ...args: any[]) => void - ): this - - addListener( - event: 'close', - listener: (code: number, reason: Buffer) => void - ): this - addListener(event: 'error', listener: (err: Error) => void): this - addListener( - event: 'upgrade', - listener: (request: IncomingMessage) => void - ): this - addListener( - event: 'message', - listener: (data: WebSocket_2.RawData, isBinary: boolean) => void - ): this - addListener(event: 'open', listener: () => void): this - addListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this - addListener( - event: 'unexpected-response', - listener: (request: ClientRequest, response: IncomingMessage) => void - ): this - addListener(event: string | symbol, listener: (...args: any[]) => void): this - - removeListener( - event: 'close', - listener: (code: number, reason: Buffer) => void - ): this - removeListener(event: 'error', listener: (err: Error) => void): this - removeListener( - event: 'upgrade', - listener: (request: IncomingMessage) => void - ): this - removeListener( - event: 'message', - listener: (data: WebSocket_2.RawData, isBinary: boolean) => void - ): this - removeListener(event: 'open', listener: () => void): this - removeListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this - removeListener( - event: 'unexpected-response', - listener: (request: ClientRequest, response: IncomingMessage) => void - ): this - removeListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this -} - -declare namespace WebSocket_2 { - /** - * Data represents the raw message payload received over the WebSocket. - */ - type RawData = Buffer | ArrayBuffer | Buffer[] - - /** - * Data represents the message payload received over the WebSocket. - */ - type Data = string | Buffer | ArrayBuffer | Buffer[] - - /** - * CertMeta represents the accepted types for certificate & key data. - */ - type CertMeta = string | string[] | Buffer | Buffer[] - - /** - * VerifyClientCallbackSync is a synchronous callback used to inspect the - * incoming message. The return value (boolean) of the function determines - * whether or not to accept the handshake. - */ - type VerifyClientCallbackSync = (info: { - origin: string - secure: boolean - req: IncomingMessage - }) => boolean - - /** - * VerifyClientCallbackAsync is an asynchronous callback used to inspect the - * incoming message. The return value (boolean) of the function determines - * whether or not to accept the handshake. - */ - type VerifyClientCallbackAsync = ( - info: { origin: string; secure: boolean; req: IncomingMessage }, - callback: ( - res: boolean, - code?: number, - message?: string, - headers?: OutgoingHttpHeaders - ) => void - ) => void - - interface ClientOptions extends SecureContextOptions { - protocol?: string | undefined - followRedirects?: boolean | undefined - generateMask?(mask: Buffer): void - handshakeTimeout?: number | undefined - maxRedirects?: number | undefined - perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined - localAddress?: string | undefined - protocolVersion?: number | undefined - headers?: { [key: string]: string } | undefined - origin?: string | undefined - agent?: Agent | undefined - host?: string | undefined - family?: number | undefined - checkServerIdentity?(servername: string, cert: CertMeta): boolean - rejectUnauthorized?: boolean | undefined - maxPayload?: number | undefined - skipUTF8Validation?: boolean | undefined - } - - interface PerMessageDeflateOptions { - serverNoContextTakeover?: boolean | undefined - clientNoContextTakeover?: boolean | undefined - serverMaxWindowBits?: number | undefined - clientMaxWindowBits?: number | undefined - zlibDeflateOptions?: - | { - flush?: number | undefined - finishFlush?: number | undefined - chunkSize?: number | undefined - windowBits?: number | undefined - level?: number | undefined - memLevel?: number | undefined - strategy?: number | undefined - dictionary?: Buffer | Buffer[] | DataView | undefined - info?: boolean | undefined - } - | undefined - zlibInflateOptions?: ZlibOptions | undefined - threshold?: number | undefined - concurrencyLimit?: number | undefined - } - - interface Event { - type: string - target: WebSocket - } - - interface ErrorEvent { - error: any - message: string - type: string - target: WebSocket - } - - interface CloseEvent { - wasClean: boolean - code: number - reason: string - type: string - target: WebSocket - } - - interface MessageEvent { - data: Data - type: string - target: WebSocket - } - - interface EventListenerOptions { - once?: boolean | undefined - } - - interface ServerOptions { - host?: string | undefined - port?: number | undefined - backlog?: number | undefined - server?: Server | Server_2 | undefined - verifyClient?: - | VerifyClientCallbackAsync - | VerifyClientCallbackSync - | undefined - handleProtocols?: ( - protocols: Set, - request: IncomingMessage - ) => string | false - path?: string | undefined - noServer?: boolean | undefined - clientTracking?: boolean | undefined - perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined - maxPayload?: number | undefined - skipUTF8Validation?: boolean | undefined - WebSocket?: typeof WebSocket.WebSocket | undefined - } - - interface AddressInfo { - address: string - family: string - port: number - } - - // WebSocket Server - class Server extends EventEmitter { - options: ServerOptions - path: string - clients: Set - - constructor(options?: ServerOptions, callback?: () => void) - - address(): AddressInfo | string - close(cb?: (err?: Error) => void): void - handleUpgrade( - request: IncomingMessage, - socket: Duplex, - upgradeHead: Buffer, - callback: (client: T, request: IncomingMessage) => void - ): void - shouldHandle(request: IncomingMessage): boolean | Promise - - // Events - on( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - on(event: 'error', cb: (this: Server, error: Error) => void): this - on( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - on(event: 'close' | 'listening', cb: (this: Server) => void): this - on( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - once( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - once(event: 'error', cb: (this: Server, error: Error) => void): this - once( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - once(event: 'close' | 'listening', cb: (this: Server) => void): this - once( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - off( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - off(event: 'error', cb: (this: Server, error: Error) => void): this - off( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - off(event: 'close' | 'listening', cb: (this: Server) => void): this - off( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - addListener( - event: 'connection', - cb: (client: T, request: IncomingMessage) => void - ): this - addListener(event: 'error', cb: (err: Error) => void): this - addListener( - event: 'headers', - cb: (headers: string[], request: IncomingMessage) => void - ): this - addListener(event: 'close' | 'listening', cb: () => void): this - addListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this - - removeListener(event: 'connection', cb: (client: T) => void): this - removeListener(event: 'error', cb: (err: Error) => void): this - removeListener( - event: 'headers', - cb: (headers: string[], request: IncomingMessage) => void - ): this - removeListener(event: 'close' | 'listening', cb: () => void): this - removeListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this - } - - const WebSocketServer: typeof Server - interface WebSocketServer extends Server {} // tslint:disable-line no-empty-interface - const WebSocket: typeof WebSocketAlias - interface WebSocket extends WebSocketAlias {} // tslint:disable-line no-empty-interface - - // WebSocket stream - function createWebSocketStream( - websocket: WebSocket, - options?: DuplexOptions - ): Duplex -} -export { WebSocket_2 as WebSocket } - -export declare const WebSocketAlias: typeof WebSocket_2; - -export declare interface WebSocketAlias extends WebSocket_2 {} - -export declare interface WebSocketClient { - /** - * Send event to the client - */ - send(payload: HMRPayload): void; - /** - * Send custom event - */ - send(event: string, payload?: CustomPayload['data']): void; - /** - * The raw WebSocket instance - * @advanced - */ - socket: WebSocket_2; -} - -export declare type WebSocketCustomListener = (data: T, client: WebSocketClient) => void; - -export declare interface WebSocketServer { - /** - * Get all connected clients. - */ - clients: Set; - /** - * Broadcast events to all clients - */ - send(payload: HMRPayload): void; - /** - * Send custom event - */ - send(event: T, payload?: InferCustomEventPayload): void; - /** - * Disconnect all clients and terminate the server. - */ - close(): Promise; - /** - * Handle custom event emitted by `import.meta.hot.send` - */ - on: WebSocket_2.Server['on'] & { - (event: T, listener: WebSocketCustomListener>): void; - }; - /** - * Unregister event listener. - */ - off: WebSocket_2.Server['off'] & { - (event: string, listener: Function): void; - }; -} - -export { } +/// + +import type { Agent } from 'node:http'; +import type { BuildOptions as BuildOptions_2 } from 'esbuild'; +import type { ClientRequest } from 'node:http'; +import type { ClientRequestArgs } from 'node:http'; +import { ConnectedPayload } from "../../types/hmrPayload"; +import { CustomEventMap } from "../../types/customEvent"; +import { CustomPayload } from "../../types/hmrPayload"; +import type { CustomPluginOptions } from 'rollup'; +import type { Duplex } from 'node:stream'; +import type { DuplexOptions } from 'node:stream'; +import { ErrorPayload } from "../../types/hmrPayload"; +import { TransformOptions as EsbuildTransformOptions } from 'esbuild'; +import { version as esbuildVersion } from 'esbuild'; +import { EventEmitter } from 'node:events'; +import * as events from 'node:events'; +import type { ExistingRawSourceMap } from 'rollup'; +import type * as fs from 'node:fs'; +import { FullReloadPayload } from "../../types/hmrPayload"; +import { GeneralImportGlobOptions } from "../../types/importGlob"; +import type { GetManualChunk } from 'rollup'; +import { HMRPayload } from "../../types/hmrPayload"; +import * as http from 'node:http'; +import { ImportGlobEagerFunction } from "../../types/importGlob"; +import { ImportGlobFunction } from "../../types/importGlob"; +import { ImportGlobOptions } from "../../types/importGlob"; +import type { IncomingMessage } from 'node:http'; +import { InferCustomEventPayload } from "../../types/customEvent"; +import type { InputOption } from 'rollup'; +import type { InputOptions } from 'rollup'; +import { InvalidatePayload } from "../../types/customEvent"; +import { KnownAsTypeMap } from "../../types/importGlob"; +import type { LoadResult } from 'rollup'; + +import type { ModuleFormat } from 'rollup'; +import type { ModuleInfo } from 'rollup'; +import type * as net from 'node:net'; +import type { ObjectHook } from 'rollup'; +import type { OutgoingHttpHeaders } from 'node:http'; +import type { OutputBundle } from 'rollup'; +import type { OutputChunk } from 'rollup'; +import type { PartialResolvedId } from 'rollup'; +import type { Plugin as Plugin_3 } from 'rollup'; +import type { PluginContext } from 'rollup'; +import type { PluginHooks } from 'rollup'; +import type * as PostCSS from 'postcss'; +import { PrunePayload } from "../../types/hmrPayload"; +import type { ResolveIdResult } from 'rollup'; +import type { RollupError } from 'rollup'; +import type { RollupOptions } from 'rollup'; +import type { RollupOutput } from 'rollup'; +import { VERSION as rollupVersion } from 'rollup'; +import type { RollupWatcher } from 'rollup'; +import type { SecureContextOptions } from 'node:tls'; +import type { Server } from 'node:http'; +import type { Server as Server_2 } from 'node:https'; +import type { ServerOptions as ServerOptions_2 } from 'node:https'; +import type { ServerResponse } from 'node:http'; +import type { SourceDescription } from 'rollup'; +import type { SourceMap } from 'rollup'; +import type { SourceMapInput } from 'rollup'; +import type * as stream from 'node:stream'; +import type { TransformPluginContext } from 'rollup'; +import type { TransformResult as TransformResult_2 } from 'rollup'; +import type { TransformResult as TransformResult_3 } from 'esbuild'; +import { Update } from "../../types/hmrPayload"; +import { UpdatePayload } from "../../types/hmrPayload"; +import type * as url from 'node:url'; +import type { URL as URL_2 } from 'node:url'; +import type { WatcherOptions } from 'rollup'; +import type { ZlibOptions } from 'node:zlib'; + +export declare interface Alias { + find: string | RegExp + replacement: string + /** + * Instructs the plugin to use an alternative resolving algorithm, + * rather than the Rollup's resolver. + * @default null + */ + customResolver?: ResolverFunction | ResolverObject | null +} + +/** + * Specifies an `Object`, or an `Array` of `Object`, + * which defines aliases used to replace values in `import` or `require` statements. + * With either format, the order of the entries is important, + * in that the first defined rules are applied first. + * + * This is passed to \@rollup/plugin-alias as the "entries" field + * https://github.com/rollup/plugins/tree/master/packages/alias#entries + */ +export declare type AliasOptions = readonly Alias[] | { [find: string]: string } + +export declare type AnymatchFn = (testString: string) => boolean + +export declare type AnymatchPattern = string | RegExp | AnymatchFn + +/** + * spa: include SPA fallback middleware and configure sirv with `single: true` in preview + * + * mpa: only include non-SPA HTML middlewares + * + * custom: don't include HTML middlewares + */ +export declare type AppType = 'spa' | 'mpa' | 'custom'; + +export declare interface AwaitWriteFinishOptions { + /** + * Amount of time in milliseconds for a file size to remain constant before emitting its event. + */ + stabilityThreshold?: number + + /** + * File size polling interval. + */ + pollInterval?: number +} + +/** + * Bundles the app for production. + * Returns a Promise containing the build result. + */ +export declare function build(inlineConfig?: InlineConfig): Promise; + +export declare interface BuildOptions { + /** + * Compatibility transform target. The transform is performed with esbuild + * and the lowest supported target is es2015/es6. Note this only handles + * syntax transformation and does not cover polyfills (except for dynamic + * import) + * + * Default: 'modules' - Similar to `@babel/preset-env`'s targets.esmodules, + * transpile targeting browsers that natively support dynamic es module imports. + * https://caniuse.com/es6-module-dynamic-import + * + * Another special value is 'esnext' - which only performs minimal transpiling + * (for minification compat) and assumes native dynamic imports support. + * + * For custom targets, see https://esbuild.github.io/api/#target and + * https://esbuild.github.io/content-types/#javascript for more details. + */ + target?: 'modules' | EsbuildTransformOptions['target'] | false; + /** + * whether to inject module preload polyfill. + * Note: does not apply to library mode. + * @default true + * @deprecated use `modulePreload.polyfill` instead + */ + polyfillModulePreload?: boolean; + /** + * Configure module preload + * Note: does not apply to library mode. + * @default true + */ + modulePreload?: boolean | ModulePreloadOptions; + /** + * Directory relative from `root` where build output will be placed. If the + * directory exists, it will be removed before the build. + * @default 'dist' + */ + outDir?: string; + /** + * Directory relative from `outDir` where the built js/css/image assets will + * be placed. + * @default 'assets' + */ + assetsDir?: string; + /** + * Static asset files smaller than this number (in bytes) will be inlined as + * base64 strings. Default limit is `4096` (4kb). Set to `0` to disable. + * @default 4096 + */ + assetsInlineLimit?: number; + /** + * Whether to code-split CSS. When enabled, CSS in async chunks will be + * inlined as strings in the chunk and inserted via dynamically created + * style tags when the chunk is loaded. + * @default true + */ + cssCodeSplit?: boolean; + /** + * An optional separate target for CSS minification. + * As esbuild only supports configuring targets to mainstream + * browsers, users may need this option when they are targeting + * a niche browser that comes with most modern JavaScript features + * but has poor CSS support, e.g. Android WeChat WebView, which + * doesn't support the #RGBA syntax. + */ + cssTarget?: EsbuildTransformOptions['target'] | false; + /** + * If `true`, a separate sourcemap file will be created. If 'inline', the + * sourcemap will be appended to the resulting output file as data URI. + * 'hidden' works like `true` except that the corresponding sourcemap + * comments in the bundled files are suppressed. + * @default false + */ + sourcemap?: boolean | 'inline' | 'hidden'; + /** + * Set to `false` to disable minification, or specify the minifier to use. + * Available options are 'terser' or 'esbuild'. + * @default 'esbuild' + */ + minify?: boolean | 'terser' | 'esbuild'; + /** + * Options for terser + * https://terser.org/docs/api-reference#minify-options + */ + terserOptions?: Terser.MinifyOptions; + /** + * Will be merged with internal rollup options. + * https://rollupjs.org/guide/en/#big-list-of-options + */ + rollupOptions?: RollupOptions; + /** + * Options to pass on to `@rollup/plugin-commonjs` + */ + commonjsOptions?: RollupCommonJSOptions; + /** + * Options to pass on to `@rollup/plugin-dynamic-import-vars` + */ + dynamicImportVarsOptions?: RollupDynamicImportVarsOptions; + /** + * Whether to write bundle to disk + * @default true + */ + write?: boolean; + /** + * Empty outDir on write. + * @default true when outDir is a sub directory of project root + */ + emptyOutDir?: boolean | null; + /** + * Copy the public directory to outDir on write. + * @default true + * @experimental + */ + copyPublicDir?: boolean; + /** + * Whether to emit a manifest.json under assets dir to map hash-less filenames + * to their hashed versions. Useful when you want to generate your own HTML + * instead of using the one generated by Vite. + * + * Example: + * + * ```json + * { + * "main.js": { + * "file": "main.68fe3fad.js", + * "css": "main.e6b63442.css", + * "imports": [...], + * "dynamicImports": [...] + * } + * } + * ``` + * @default false + */ + manifest?: boolean | string; + /** + * Build in library mode. The value should be the global name of the lib in + * UMD mode. This will produce esm + cjs + umd bundle formats with default + * configurations that are suitable for distributing libraries. + */ + lib?: LibraryOptions | false; + /** + * Produce SSR oriented build. Note this requires specifying SSR entry via + * `rollupOptions.input`. + */ + ssr?: boolean | string; + /** + * Generate SSR manifest for determining style links and asset preload + * directives in production. + */ + ssrManifest?: boolean | string; + /** + * Set to false to disable reporting compressed chunk sizes. + * Can slightly improve build speed. + */ + reportCompressedSize?: boolean; + /** + * Adjust chunk size warning limit (in kbs). + * @default 500 + */ + chunkSizeWarningLimit?: number; + /** + * Rollup watch options + * https://rollupjs.org/guide/en/#watchoptions + */ + watch?: WatcherOptions | null; +} + +export declare interface ChunkMetadata { + importedAssets: Set; + importedCss: Set; +} + +export declare interface CommonServerOptions { + /** + * Specify server port. Note if the port is already being used, Vite will + * automatically try the next available port so this may not be the actual + * port the server ends up listening on. + */ + port?: number; + /** + * If enabled, vite will exit if specified port is already in use + */ + strictPort?: boolean; + /** + * Specify which IP addresses the server should listen on. + * Set to 0.0.0.0 to listen on all addresses, including LAN and public addresses. + */ + host?: string | boolean; + /** + * Enable TLS + HTTP/2. + * Note: this downgrades to TLS only when the proxy option is also used. + */ + https?: boolean | ServerOptions_2; + /** + * Open browser window on startup + */ + open?: boolean | string; + /** + * Configure custom proxy rules for the dev server. Expects an object + * of `{ key: options }` pairs. + * Uses [`http-proxy`](https://github.com/http-party/node-http-proxy). + * Full options [here](https://github.com/http-party/node-http-proxy#options). + * + * Example `vite.config.js`: + * ``` js + * module.exports = { + * proxy: { + * // string shorthand + * '/foo': 'http://localhost:4567/foo', + * // with options + * '/api': { + * target: 'http://jsonplaceholder.typicode.com', + * changeOrigin: true, + * rewrite: path => path.replace(/^\/api/, '') + * } + * } + * } + * ``` + */ + proxy?: Record; + /** + * Configure CORS for the dev server. + * Uses https://github.com/expressjs/cors. + * Set to `true` to allow all methods from any origin, or configure separately + * using an object. + */ + cors?: CorsOptions | boolean; + /** + * Specify server response headers. + */ + headers?: OutgoingHttpHeaders; +} + +export declare interface ConfigEnv { + command: 'build' | 'serve'; + mode: string; + /** + * @experimental + */ + ssrBuild?: boolean; +} + +export declare namespace Connect { + export type ServerHandle = HandleFunction | http.Server + + export class IncomingMessage extends http.IncomingMessage { + originalUrl?: http.IncomingMessage['url'] | undefined + } + + export type NextFunction = (err?: any) => void + + export type SimpleHandleFunction = ( + req: IncomingMessage, + res: http.ServerResponse + ) => void + export type NextHandleFunction = ( + req: IncomingMessage, + res: http.ServerResponse, + next: NextFunction + ) => void + export type ErrorHandleFunction = ( + err: any, + req: IncomingMessage, + res: http.ServerResponse, + next: NextFunction + ) => void + export type HandleFunction = + | SimpleHandleFunction + | NextHandleFunction + | ErrorHandleFunction + + export interface ServerStackItem { + route: string + handle: ServerHandle + } + + export interface Server extends NodeJS.EventEmitter { + (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void + + route: string + stack: ServerStackItem[] + + /** + * Utilize the given middleware `handle` to the given `route`, + * defaulting to _/_. This "route" is the mount-point for the + * middleware, when given a value other than _/_ the middleware + * is only effective when that segment is present in the request's + * pathname. + * + * For example if we were to mount a function at _/admin_, it would + * be invoked on _/admin_, and _/admin/settings_, however it would + * not be invoked for _/_, or _/posts_. + */ + use(fn: NextHandleFunction): Server + use(fn: HandleFunction): Server + use(route: string, fn: NextHandleFunction): Server + use(route: string, fn: HandleFunction): Server + + /** + * Handle server requests, punting them down + * the middleware stack. + */ + handle( + req: http.IncomingMessage, + res: http.ServerResponse, + next: Function + ): void + + /** + * Listen for connections. + * + * This method takes the same arguments + * as node's `http.Server#listen()`. + * + * HTTP and HTTPS: + * + * If you run your application both as HTTP + * and HTTPS you may wrap them individually, + * since your Connect "server" is really just + * a JavaScript `Function`. + * + * var connect = require('connect') + * , http = require('http') + * , https = require('https'); + * + * var app = connect(); + * + * http.createServer(app).listen(80); + * https.createServer(options, app).listen(443); + */ + listen( + port: number, + hostname?: string, + backlog?: number, + callback?: Function + ): http.Server + listen(port: number, hostname?: string, callback?: Function): http.Server + listen(path: string, callback?: Function): http.Server + listen(handle: any, listeningListener?: Function): http.Server + } +} + +export { ConnectedPayload } + +/** + * https://github.com/expressjs/cors#configuration-options + */ +export declare interface CorsOptions { + origin?: CorsOrigin | ((origin: string, cb: (err: Error, origins: CorsOrigin) => void) => void); + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + preflightContinue?: boolean; + optionsSuccessStatus?: number; +} + +export declare type CorsOrigin = boolean | string | RegExp | (string | RegExp)[]; + +export declare const createFilter: (include?: FilterPattern | undefined, exclude?: FilterPattern | undefined, options?: { + resolve?: string | false | null | undefined; +} | undefined) => (id: string | unknown) => boolean; + +export declare function createLogger(level?: LogLevel, options?: LoggerOptions): Logger; + +export declare function createServer(inlineConfig?: InlineConfig): Promise; + +export declare interface CSSModulesOptions { + getJSON?: (cssFileName: string, json: Record, outputFileName: string) => void; + scopeBehaviour?: 'global' | 'local'; + globalModulePaths?: RegExp[]; + generateScopedName?: string | ((name: string, filename: string, css: string) => string); + hashPrefix?: string; + /** + * default: null + */ + localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' | null; +} + +export declare interface CSSOptions { + /** + * https://github.com/css-modules/postcss-modules + */ + modules?: CSSModulesOptions | false; + preprocessorOptions?: Record; + postcss?: string | (PostCSS.ProcessOptions & { + plugins?: PostCSS.AcceptedPlugin[]; + }); + /** + * Enables css sourcemaps during dev + * @default false + * @experimental + */ + devSourcemap?: boolean; +} + +export { CustomEventMap } + +export { CustomPayload } + +/** + * Type helper to make it easier to use vite.config.ts + * accepts a direct {@link UserConfig} object, or a function that returns it. + * The function receives a {@link ConfigEnv} object that exposes two properties: + * `command` (either `'build'` or `'serve'`), and `mode`. + */ +export declare function defineConfig(config: UserConfigExport): UserConfigExport; + +export declare interface DepOptimizationConfig { + /** + * Force optimize listed dependencies (must be resolvable import paths, + * cannot be globs). + */ + include?: string[]; + /** + * Do not optimize these dependencies (must be resolvable import paths, + * cannot be globs). + */ + exclude?: string[]; + /** + * Force ESM interop when importing for these dependencies. Some legacy + * packages advertise themselves as ESM but use `require` internally + * @experimental + */ + needsInterop?: string[]; + /** + * Options to pass to esbuild during the dep scanning and optimization + * + * Certain options are omitted since changing them would not be compatible + * with Vite's dep optimization. + * + * - `external` is also omitted, use Vite's `optimizeDeps.exclude` option + * - `plugins` are merged with Vite's dep plugin + * + * https://esbuild.github.io/api + */ + esbuildOptions?: Omit; + /** + * List of file extensions that can be optimized. A corresponding esbuild + * plugin must exist to handle the specific extension. + * + * By default, Vite can optimize `.mjs`, `.js`, `.ts`, and `.mts` files. This option + * allows specifying additional extensions. + * + * @experimental + */ + extensions?: string[]; + /** + * Disables dependencies optimizations, true disables the optimizer during + * build and dev. Pass 'build' or 'dev' to only disable the optimizer in + * one of the modes. Deps optimization is enabled by default in dev only. + * @default 'build' + * @experimental + */ + disabled?: boolean | 'build' | 'dev'; +} + +export declare interface DepOptimizationMetadata { + /** + * The main hash is determined by user config and dependency lockfiles. + * This is checked on server startup to avoid unnecessary re-bundles. + */ + hash: string; + /** + * The browser hash is determined by the main hash plus additional dependencies + * discovered at runtime. This is used to invalidate browser requests to + * optimized deps. + */ + browserHash: string; + /** + * Metadata for each already optimized dependency + */ + optimized: Record; + /** + * Metadata for non-entry optimized chunks and dynamic imports + */ + chunks: Record; + /** + * Metadata for each newly discovered dependency after processing + */ + discovered: Record; + /** + * OptimizedDepInfo list + */ + depInfoList: OptimizedDepInfo[]; +} + +export declare type DepOptimizationOptions = DepOptimizationConfig & { + /** + * By default, Vite will crawl your `index.html` to detect dependencies that + * need to be pre-bundled. If `build.rollupOptions.input` is specified, Vite + * will crawl those entry points instead. + * + * If neither of these fit your needs, you can specify custom entries using + * this option - the value should be a fast-glob pattern or array of patterns + * (https://github.com/mrmlnc/fast-glob#basic-syntax) that are relative from + * vite project root. This will overwrite default entries inference. + */ + entries?: string | string[]; + /** + * Force dep pre-optimization regardless of whether deps have changed. + * @experimental + */ + force?: boolean; +}; + +export declare interface DepOptimizationProcessing { + promise: Promise; + resolve: () => void; +} + +export declare interface DepOptimizationResult { + metadata: DepOptimizationMetadata; + /** + * When doing a re-run, if there are newly discovered dependencies + * the page reload will be delayed until the next rerun so we need + * to be able to discard the result + */ + commit: () => Promise; + cancel: () => void; +} + +export declare interface DepsOptimizer { + metadata: DepOptimizationMetadata; + scanProcessing?: Promise; + registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo; + run: () => void; + isOptimizedDepFile: (id: string) => boolean; + isOptimizedDepUrl: (url: string) => boolean; + getOptimizedDepId: (depInfo: OptimizedDepInfo) => string; + delayDepsOptimizerUntil: (id: string, done: () => Promise) => void; + registerWorkersSource: (id: string) => void; + resetRegisteredIds: () => void; + ensureFirstRun: () => void; + close: () => Promise; + options: DepOptimizationOptions; +} + +export { ErrorPayload } + +export declare interface ESBuildOptions extends EsbuildTransformOptions { + include?: string | RegExp | string[] | RegExp[]; + exclude?: string | RegExp | string[] | RegExp[]; + jsxInject?: string; + /** + * This option is not respected. Use `build.minify` instead. + */ + minify?: never; +} + +export { EsbuildTransformOptions } + +export declare type ESBuildTransformResult = Omit & { + map: SourceMap; +}; + +export { esbuildVersion } + +export declare interface ExperimentalOptions { + /** + * Append fake `&lang.(ext)` when queries are specified, to preserve the file extension for following plugins to process. + * + * @experimental + * @default false + */ + importGlobRestoreExtension?: boolean; + /** + * Allow finegrain control over assets and public files paths + * + * @experimental + */ + renderBuiltUrl?: RenderBuiltAssetUrl; + /** + * Enables support of HMR partial accept via `import.meta.hot.acceptExports`. + * + * @experimental + * @default false + */ + hmrPartialAccept?: boolean; +} + +export declare type ExportsData = { + hasImports: boolean; + exports: readonly string[]; + facade: boolean; + hasReExports?: boolean; + jsxLoader?: boolean; +}; + +export declare interface FileSystemServeOptions { + /** + * Strictly restrict file accessing outside of allowing paths. + * + * Set to `false` to disable the warning + * + * @default true + */ + strict?: boolean; + /** + * Restrict accessing files outside the allowed directories. + * + * Accepts absolute path or a path relative to project root. + * Will try to search up for workspace root by default. + */ + allow?: string[]; + /** + * Restrict accessing files that matches the patterns. + * + * This will have higher priority than `allow`. + * picomatch patterns are supported. + * + * @default ['.env', '.env.*', '*.crt', '*.pem'] + */ + deny?: string[]; +} + +/** + * Inlined to keep `@rollup/pluginutils` in devDependencies + */ +export declare type FilterPattern = ReadonlyArray | string | RegExp | null; + +export declare function formatPostcssSourceMap(rawMap: ExistingRawSourceMap, file: string): Promise; + +export declare class FSWatcher extends EventEmitter implements fs.FSWatcher { + options: WatchOptions + + /** + * Constructs a new FSWatcher instance with optional WatchOptions parameter. + */ + constructor(options?: WatchOptions) + + /** + * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one + * string. + */ + add(paths: string | ReadonlyArray): this + + /** + * Stop watching files, directories, or glob patterns. Takes an array of strings or just one + * string. + */ + unwatch(paths: string | ReadonlyArray): this + + /** + * Returns an object representing all the paths on the file system being watched by this + * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless + * the `cwd` option was used), and the values are arrays of the names of the items contained in + * each directory. + */ + getWatched(): { + [directory: string]: string[] + } + + /** + * Removes all listeners from watched files. + */ + close(): Promise + + on( + event: 'add' | 'addDir' | 'change', + listener: (path: string, stats?: fs.Stats) => void + ): this + + on( + event: 'all', + listener: ( + eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', + path: string, + stats?: fs.Stats + ) => void + ): this + + /** + * Error occurred + */ + on(event: 'error', listener: (error: Error) => void): this + + /** + * Exposes the native Node `fs.FSWatcher events` + */ + on( + event: 'raw', + listener: (eventName: string, path: string, details: any) => void + ): this + + /** + * Fires when the initial scan is complete + */ + on(event: 'ready', listener: () => void): this + + on(event: 'unlink' | 'unlinkDir', listener: (path: string) => void): this + + on(event: string, listener: (...args: any[]) => void): this +} + +export { FullReloadPayload } + +export { GeneralImportGlobOptions } + +export declare function getDepOptimizationConfig(config: ResolvedConfig, ssr: boolean): DepOptimizationConfig; + +export declare interface HmrContext { + file: string; + timestamp: number; + modules: Array; + read: () => string | Promise; + server: ViteDevServer; +} + +export declare interface HmrOptions { + protocol?: string; + host?: string; + port?: number; + clientPort?: number; + path?: string; + timeout?: number; + overlay?: boolean; + server?: Server; +} + +export { HMRPayload } + +export declare type HookHandler = T extends ObjectHook ? H : T; + +export declare interface HtmlTagDescriptor { + tag: string; + attrs?: Record; + children?: string | HtmlTagDescriptor[]; + /** + * default: 'head-prepend' + */ + injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend'; +} + +export declare namespace HttpProxy { + export type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed + + export type ProxyTargetUrl = string | Partial + + export interface ProxyTargetDetailed { + host: string + port: number + protocol?: string | undefined + hostname?: string | undefined + socketPath?: string | undefined + key?: string | undefined + passphrase?: string | undefined + pfx?: Buffer | string | undefined + cert?: string | undefined + ca?: string | undefined + ciphers?: string | undefined + secureProtocol?: string | undefined + } + + export type ErrorCallback = ( + err: Error, + req: http.IncomingMessage, + res: http.ServerResponse, + target?: ProxyTargetUrl + ) => void + + export class Server extends events.EventEmitter { + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + */ + constructor(options?: ServerOptions) + + /** + * Used for proxying regular HTTP(S) requests + * @param req - Client request. + * @param res - Client response. + * @param options - Additional options. + */ + web( + req: http.IncomingMessage, + res: http.ServerResponse, + options?: ServerOptions, + callback?: ErrorCallback + ): void + + /** + * Used for proxying regular HTTP(S) requests + * @param req - Client request. + * @param socket - Client socket. + * @param head - Client head. + * @param options - Additional options. + */ + ws( + req: http.IncomingMessage, + socket: unknown, + head: unknown, + options?: ServerOptions, + callback?: ErrorCallback + ): void + + /** + * A function that wraps the object in a webserver, for your convenience + * @param port - Port to listen on + */ + listen(port: number): Server + + /** + * A function that closes the inner webserver and stops listening on given port + */ + close(callback?: () => void): void + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createProxyServer(options?: ServerOptions): Server + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createServer(options?: ServerOptions): Server + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createProxy(options?: ServerOptions): Server + + addListener(event: string, listener: () => void): this + on(event: string, listener: () => void): this + on(event: 'error', listener: ErrorCallback): this + on( + event: 'start', + listener: ( + req: http.IncomingMessage, + res: http.ServerResponse, + target: ProxyTargetUrl + ) => void + ): this + on( + event: 'proxyReq', + listener: ( + proxyReq: http.ClientRequest, + req: http.IncomingMessage, + res: http.ServerResponse, + options: ServerOptions + ) => void + ): this + on( + event: 'proxyRes', + listener: ( + proxyRes: http.IncomingMessage, + req: http.IncomingMessage, + res: http.ServerResponse + ) => void + ): this + on( + event: 'proxyReqWs', + listener: ( + proxyReq: http.ClientRequest, + req: http.IncomingMessage, + socket: net.Socket, + options: ServerOptions, + head: any + ) => void + ): this + on( + event: 'econnreset', + listener: ( + err: Error, + req: http.IncomingMessage, + res: http.ServerResponse, + target: ProxyTargetUrl + ) => void + ): this + on( + event: 'end', + listener: ( + req: http.IncomingMessage, + res: http.ServerResponse, + proxyRes: http.IncomingMessage + ) => void + ): this + on( + event: 'close', + listener: ( + proxyRes: http.IncomingMessage, + proxySocket: net.Socket, + proxyHead: any + ) => void + ): this + + once(event: string, listener: () => void): this + removeListener(event: string, listener: () => void): this + removeAllListeners(event?: string): this + getMaxListeners(): number + setMaxListeners(n: number): this + listeners(event: string): Array<() => void> + emit(event: string, ...args: any[]): boolean + listenerCount(type: string): number + } + + export interface ServerOptions { + /** URL string to be parsed with the url module. */ + target?: ProxyTarget | undefined + /** URL string to be parsed with the url module. */ + forward?: ProxyTargetUrl | undefined + /** Object to be passed to http(s).request. */ + agent?: any + /** Object to be passed to https.createServer(). */ + ssl?: any + /** If you want to proxy websockets. */ + ws?: boolean | undefined + /** Adds x- forward headers. */ + xfwd?: boolean | undefined + /** Verify SSL certificate. */ + secure?: boolean | undefined + /** Explicitly specify if we are proxying to another proxy. */ + toProxy?: boolean | undefined + /** Specify whether you want to prepend the target's path to the proxy path. */ + prependPath?: boolean | undefined + /** Specify whether you want to ignore the proxy path of the incoming request. */ + ignorePath?: boolean | undefined + /** Local interface string to bind for outgoing connections. */ + localAddress?: string | undefined + /** Changes the origin of the host header to the target URL. */ + changeOrigin?: boolean | undefined + /** specify whether you want to keep letter case of response header key */ + preserveHeaderKeyCase?: boolean | undefined + /** Basic authentication i.e. 'user:password' to compute an Authorization header. */ + auth?: string | undefined + /** Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null. */ + hostRewrite?: string | undefined + /** Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false. */ + autoRewrite?: boolean | undefined + /** Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null. */ + protocolRewrite?: string | undefined + /** rewrites domain of set-cookie headers. */ + cookieDomainRewrite?: + | false + | string + | { [oldDomain: string]: string } + | undefined + /** rewrites path of set-cookie headers. Default: false */ + cookiePathRewrite?: + | false + | string + | { [oldPath: string]: string } + | undefined + /** object with extra headers to be added to target requests. */ + headers?: { [header: string]: string } | undefined + /** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */ + proxyTimeout?: number | undefined + /** Timeout (in milliseconds) for incoming requests */ + timeout?: number | undefined + /** Specify whether you want to follow redirects. Default: false */ + followRedirects?: boolean | undefined + /** If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event */ + selfHandleResponse?: boolean | undefined + /** Buffer */ + buffer?: stream.Stream | undefined + } +} + +export { ImportGlobEagerFunction } + +export { ImportGlobFunction } + +export { ImportGlobOptions } + +export declare type IndexHtmlTransform = IndexHtmlTransformHook | { + enforce?: 'pre' | 'post'; + transform: IndexHtmlTransformHook; +}; + +export declare interface IndexHtmlTransformContext { + /** + * public path when served + */ + path: string; + /** + * filename on disk + */ + filename: string; + server?: ViteDevServer; + bundle?: OutputBundle; + chunk?: OutputChunk; + originalUrl?: string; +} + +export declare type IndexHtmlTransformHook = (this: void, html: string, ctx: IndexHtmlTransformContext) => IndexHtmlTransformResult | void | Promise; + +export declare type IndexHtmlTransformResult = string | HtmlTagDescriptor[] | { + html: string; + tags: HtmlTagDescriptor[]; +}; + +export { InferCustomEventPayload } + +export declare interface InlineConfig extends UserConfig { + configFile?: string | false; + envFile?: false; +} + +export declare interface InternalResolveOptions extends Required { + root: string; + isBuild: boolean; + isProduction: boolean; + ssrConfig?: SSROptions; + packageCache?: PackageCache; + /** + * src code mode also attempts the following: + * - resolving /xxx as URLs + * - resolving bare imports from optimized deps + */ + asSrc?: boolean; + tryIndex?: boolean; + tryPrefix?: string; + skipPackageJson?: boolean; + preferRelative?: boolean; + isRequire?: boolean; + isFromTsImporter?: boolean; + tryEsmOnly?: boolean; + scan?: boolean; + ssrOptimizeCheck?: boolean; + getDepsOptimizer?: (ssr: boolean) => DepsOptimizer | undefined; + shouldExternalize?: (id: string) => boolean | undefined; + isHookNodeResolve?: boolean; +} + +export { InvalidatePayload } + +export declare function isDepsOptimizerEnabled(config: ResolvedConfig, ssr: boolean): boolean; + +export declare interface JsonOptions { + /** + * Generate a named export for every property of the JSON object + * @default true + */ + namedExports?: boolean; + /** + * Generate performant output as JSON.parse("stringified"). + * Enabling this will disable namedExports. + * @default false + */ + stringify?: boolean; +} + +export { KnownAsTypeMap } + +export declare interface LegacyOptions { + /** + * Revert vite build --ssr to the v2.9 strategy. Use CJS SSR build and v2.9 externalization heuristics + * + * @experimental + * @deprecated + * @default false + */ + buildSsrCjsExternalHeuristics?: boolean; +} + +export declare type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife'; + +export declare interface LibraryOptions { + /** + * Path of library entry + */ + entry: InputOption; + /** + * The name of the exposed global variable. Required when the `formats` option includes + * `umd` or `iife` + */ + name?: string; + /** + * Output bundle formats + * @default ['es', 'umd'] + */ + formats?: LibraryFormats[]; + /** + * The name of the package file output. The default file name is the name option + * of the project package.json. It can also be defined as a function taking the + * format as an argument. + */ + fileName?: string | ((format: ModuleFormat, entryName: string) => string); +} + +export declare function loadConfigFromFile(configEnv: ConfigEnv, configFile?: string, configRoot?: string, logLevel?: LogLevel): Promise<{ + path: string; + config: UserConfig; + dependencies: string[]; +} | null>; + +export declare function loadEnv(mode: string, envDir: string, prefixes?: string | string[]): Record; + +export declare interface LogErrorOptions extends LogOptions { + error?: Error | RollupError | null; +} + +export declare interface Logger { + info(msg: string, options?: LogOptions): void; + warn(msg: string, options?: LogOptions): void; + warnOnce(msg: string, options?: LogOptions): void; + error(msg: string, options?: LogErrorOptions): void; + clearScreen(type: LogType): void; + hasErrorLogged(error: Error | RollupError): boolean; + hasWarned: boolean; +} + +export declare interface LoggerOptions { + prefix?: string; + allowClearScreen?: boolean; + customLogger?: Logger; +} + +export declare type LogLevel = LogType | 'silent'; + +export declare interface LogOptions { + clear?: boolean; + timestamp?: boolean; +} + +export declare type LogType = 'error' | 'warn' | 'info'; + +export declare type Manifest = Record; + +export declare interface ManifestChunk { + src?: string; + file: string; + css?: string[]; + assets?: string[]; + isEntry?: boolean; + isDynamicEntry?: boolean; + imports?: string[]; + dynamicImports?: string[]; +} + +export declare type MapToFunction = T extends Function ? T : never + +export declare type Matcher = AnymatchPattern | AnymatchPattern[] + +export declare function mergeAlias(a?: AliasOptions, b?: AliasOptions): AliasOptions | undefined; + +export declare function mergeConfig(defaults: Record, overrides: Record, isRoot?: boolean): Record; + +export declare class ModuleGraph { + private resolveId; + urlToModuleMap: Map; + idToModuleMap: Map; + fileToModulesMap: Map>; + safeModulesPath: Set; + constructor(resolveId: (url: string, ssr: boolean) => Promise); + getModuleByUrl(rawUrl: string, ssr?: boolean): Promise; + getModuleById(id: string): ModuleNode | undefined; + getModulesByFile(file: string): Set | undefined; + onFileChange(file: string): void; + invalidateModule(mod: ModuleNode, seen?: Set, timestamp?: number): void; + invalidateAll(): void; + /** + * Update the module graph based on a module's updated imports information + * If there are dependencies that no longer have any importers, they are + * returned as a Set. + */ + updateModuleInfo(mod: ModuleNode, importedModules: Set, importedBindings: Map> | null, acceptedModules: Set, acceptedExports: Set | null, isSelfAccepting: boolean, ssr?: boolean): Promise | undefined>; + ensureEntryFromUrl(rawUrl: string, ssr?: boolean, setIsSelfAccepting?: boolean): Promise; + createFileOnlyEntry(file: string): ModuleNode; + resolveUrl(url: string, ssr?: boolean): Promise; +} + +export declare class ModuleNode { + /** + * Public served url path, starts with / + */ + url: string; + /** + * Resolved file system path + query + */ + id: string | null; + file: string | null; + type: 'js' | 'css'; + info?: ModuleInfo; + meta?: Record; + importers: Set; + importedModules: Set; + acceptedHmrDeps: Set; + acceptedHmrExports: Set | null; + importedBindings: Map> | null; + isSelfAccepting?: boolean; + transformResult: TransformResult | null; + ssrTransformResult: TransformResult | null; + ssrModule: Record | null; + ssrError: Error | null; + lastHMRTimestamp: number; + lastInvalidationTimestamp: number; + /** + * @param setIsSelfAccepting - set `false` to set `isSelfAccepting` later. e.g. #7870 + */ + constructor(url: string, setIsSelfAccepting?: boolean); +} + +export declare interface ModulePreloadOptions { + /** + * Whether to inject a module preload polyfill. + * Note: does not apply to library mode. + * @default true + */ + polyfill?: boolean; + /** + * Resolve the list of dependencies to preload for a given dynamic import + * @experimental + */ + resolveDependencies?: ResolveModulePreloadDependenciesFn; +} + +export declare function normalizePath(id: string): string; + +export declare interface OptimizedDepInfo { + id: string; + file: string; + src?: string; + needsInterop?: boolean; + browserHash?: string; + fileHash?: string; + /** + * During optimization, ids can still be resolved to their final location + * but the bundles may not yet be saved to disk + */ + processing?: Promise; + /** + * ExportData cache, discovered deps will parse the src entry to get exports + * data used both to define if interop is needed and when pre-bundling + */ + exportsData?: Promise; +} + +/** + * Scan and optimize dependencies within a project. + * Used by Vite CLI when running `vite optimize`. + */ +export declare function optimizeDeps(config: ResolvedConfig, force?: boolean | undefined, asCommand?: boolean): Promise; + +/** Cache for package.json resolution and package.json contents */ +export declare type PackageCache = Map; + +export declare interface PackageData { + dir: string; + hasSideEffects: (id: string) => boolean | 'no-treeshake'; + webResolvedImports: Record; + nodeResolvedImports: Record; + setResolvedCache: (key: string, entry: string, targetWeb: boolean) => void; + getResolvedCache: (key: string, targetWeb: boolean) => string | undefined; + data: { + [field: string]: any; + name: string; + type: string; + version: string; + main: string; + module: string; + browser: string | Record; + exports: string | Record | string[]; + dependencies: Record; + }; +} + +/** + * Vite plugins extends the Rollup plugin interface with a few extra + * vite-specific options. A valid vite plugin is also a valid Rollup plugin. + * On the contrary, a Rollup plugin may or may NOT be a valid vite universal + * plugin, since some Rollup features do not make sense in an unbundled + * dev server context. That said, as long as a rollup plugin doesn't have strong + * coupling between its bundle phase and output phase hooks then it should + * just work (that means, most of them). + * + * By default, the plugins are run during both serve and build. When a plugin + * is applied during serve, it will only run **non output plugin hooks** (see + * rollup type definition of {@link rollup#PluginHooks}). You can think of the + * dev server as only running `const bundle = rollup.rollup()` but never calling + * `bundle.generate()`. + * + * A plugin that expects to have different behavior depending on serve/build can + * export a factory function that receives the command being run via options. + * + * If a plugin should be applied only for server or build, a function format + * config file can be used to conditional determine the plugins to use. + */ +declare interface Plugin_2 extends Plugin_3 { + /** + * Enforce plugin invocation tier similar to webpack loaders. + * + * Plugin invocation order: + * - alias resolution + * - `enforce: 'pre'` plugins + * - vite core plugins + * - normal plugins + * - vite build plugins + * - `enforce: 'post'` plugins + * - vite build post plugins + */ + enforce?: 'pre' | 'post'; + /** + * Apply the plugin only for serve or build, or on certain conditions. + */ + apply?: 'serve' | 'build' | ((this: void, config: UserConfig, env: ConfigEnv) => boolean); + /** + * Modify vite config before it's resolved. The hook can either mutate the + * passed-in config directly, or return a partial config object that will be + * deeply merged into existing config. + * + * Note: User plugins are resolved before running this hook so injecting other + * plugins inside the `config` hook will have no effect. + */ + config?: ObjectHook<(this: void, config: UserConfig, env: ConfigEnv) => UserConfig | null | void | Promise>; + /** + * Use this hook to read and store the final resolved vite config. + */ + configResolved?: ObjectHook<(this: void, config: ResolvedConfig) => void | Promise>; + /** + * Configure the vite server. The hook receives the {@link ViteDevServer} + * instance. This can also be used to store a reference to the server + * for use in other hooks. + * + * The hooks will be called before internal middlewares are applied. A hook + * can return a post hook that will be called after internal middlewares + * are applied. Hook can be async functions and will be called in series. + */ + configureServer?: ObjectHook; + /** + * Configure the preview server. The hook receives the connect server and + * its underlying http server. + * + * The hooks are called before other middlewares are applied. A hook can + * return a post hook that will be called after other middlewares are + * applied. Hooks can be async functions and will be called in series. + */ + configurePreviewServer?: ObjectHook; + /** + * Transform index.html. + * The hook receives the following arguments: + * + * - html: string + * - ctx?: vite.ServerContext (only present during serve) + * - bundle?: rollup.OutputBundle (only present during build) + * + * It can either return a transformed string, or a list of html tag + * descriptors that will be injected into the `` or ``. + * + * By default the transform is applied **after** vite's internal html + * transform. If you need to apply the transform before vite, use an object: + * `{ enforce: 'pre', transform: hook }` + */ + transformIndexHtml?: IndexHtmlTransform; + /** + * Perform custom handling of HMR updates. + * The handler receives a context containing changed filename, timestamp, a + * list of modules affected by the file change, and the dev server instance. + * + * - The hook can return a filtered list of modules to narrow down the update. + * e.g. for a Vue SFC, we can narrow down the part to update by comparing + * the descriptors. + * + * - The hook can also return an empty array and then perform custom updates + * by sending a custom hmr payload via server.ws.send(). + * + * - If the hook doesn't return a value, the hmr update will be performed as + * normal. + */ + handleHotUpdate?: ObjectHook<(this: void, ctx: HmrContext) => Array | void | Promise | void>>; + /** + * extend hooks with ssr flag + */ + resolveId?: ObjectHook<(this: PluginContext, source: string, importer: string | undefined, options: { + custom?: CustomPluginOptions; + ssr?: boolean; + /* Excluded from this release type: scan */ + isEntry: boolean; + }) => Promise | ResolveIdResult>; + load?: ObjectHook<(this: PluginContext, id: string, options?: { + ssr?: boolean; + }) => Promise | LoadResult>; + transform?: ObjectHook<(this: TransformPluginContext, code: string, id: string, options?: { + ssr?: boolean; + }) => Promise | TransformResult_2>; +} +export { Plugin_2 as Plugin } + +export declare interface PluginContainer { + options: InputOptions; + getModuleInfo(id: string): ModuleInfo | null; + buildStart(options: InputOptions): Promise; + resolveId(id: string, importer?: string, options?: { + custom?: CustomPluginOptions; + skip?: Set; + ssr?: boolean; + /* Excluded from this release type: scan */ + isEntry?: boolean; + }): Promise; + transform(code: string, id: string, options?: { + inMap?: SourceDescription['map']; + ssr?: boolean; + }): Promise; + load(id: string, options?: { + ssr?: boolean; + }): Promise; + close(): Promise; +} + +export declare interface PluginHookUtils { + getSortedPlugins: (hookName: keyof Plugin_2) => Plugin_2[]; + getSortedPluginHooks: (hookName: K) => NonNullable>[]; +} + +export declare type PluginOption = Plugin_2 | false | null | undefined | PluginOption[] | Promise; + +/** + * @experimental + */ +export declare function preprocessCSS(code: string, filename: string, config: ResolvedConfig): Promise; + +export declare interface PreprocessCSSResult { + code: string; + map?: SourceMapInput; + modules?: Record; + deps?: Set; +} + +/** + * Starts the Vite server in preview mode, to simulate a production deployment + */ +export declare function preview(inlineConfig?: InlineConfig): Promise; + +export declare interface PreviewOptions extends CommonServerOptions { +} + +export declare interface PreviewServer { + /** + * The resolved vite config object + */ + config: ResolvedConfig; + /** + * native Node http server instance + */ + httpServer: http.Server; + /** + * The resolved urls Vite prints on the CLI + */ + resolvedUrls: ResolvedServerUrls; + /** + * Print server urls + */ + printUrls(): void; +} + +export declare type PreviewServerHook = (this: void, server: { + middlewares: Connect.Server; + httpServer: http.Server; +}) => (() => void) | void | Promise<(() => void) | void>; + +export declare interface ProxyOptions extends HttpProxy.ServerOptions { + /** + * rewrite path + */ + rewrite?: (path: string) => string; + /** + * configure the proxy server (e.g. listen to events) + */ + configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void; + /** + * webpack-dev-server style bypass function + */ + bypass?: (req: http.IncomingMessage, res: http.ServerResponse, options: ProxyOptions) => void | null | undefined | false | string; +} + +export { PrunePayload } + +export declare type RenderBuiltAssetUrl = (filename: string, type: { + type: 'asset' | 'public'; + hostId: string; + hostType: 'js' | 'css' | 'html'; + ssr: boolean; +}) => string | { + relative?: boolean; + runtime?: string; +} | undefined; + +/** + * Resolve base url. Note that some users use Vite to build for non-web targets like + * electron or expects to deploy + */ +export declare function resolveBaseUrl(base: string | undefined, isBuild: boolean, logger: Logger): string; + +export declare function resolveConfig(inlineConfig: InlineConfig, command: 'build' | 'serve', defaultMode?: string): Promise; + +export declare interface ResolvedBuildOptions extends Required> { + modulePreload: false | ResolvedModulePreloadOptions; +} + +export declare type ResolvedConfig = Readonly & { + configFile: string | undefined; + configFileDependencies: string[]; + inlineConfig: InlineConfig; + root: string; + base: string; + publicDir: string; + cacheDir: string; + command: 'build' | 'serve'; + mode: string; + isWorker: boolean; + /* Excluded from this release type: mainConfig */ + isProduction: boolean; + env: Record; + resolve: Required & { + alias: Alias[]; + }; + plugins: readonly Plugin_2[]; + server: ResolvedServerOptions; + build: ResolvedBuildOptions; + preview: ResolvedPreviewOptions; + ssr: ResolvedSSROptions; + assetsInclude: (file: string) => boolean; + logger: Logger; + createResolver: (options?: Partial) => ResolveFn; + optimizeDeps: DepOptimizationOptions; + /* Excluded from this release type: packageCache */ + worker: ResolveWorkerOptions; + appType: AppType; + experimental: ExperimentalOptions; +} & PluginHookUtils>; + +export declare interface ResolvedModulePreloadOptions { + polyfill: boolean; + resolveDependencies?: ResolveModulePreloadDependenciesFn; +} + +export declare interface ResolvedPreviewOptions extends PreviewOptions { +} + +export declare interface ResolvedServerOptions extends ServerOptions { + fs: Required; + middlewareMode: boolean; +} + +export declare interface ResolvedServerUrls { + local: string[]; + network: string[]; +} + +export declare interface ResolvedSSROptions extends SSROptions { + target: SSRTarget; + format: SSRFormat; + optimizeDeps: SsrDepOptimizationOptions; +} + +export declare type ResolvedUrl = [ +url: string, +resolvedId: string, +meta: object | null | undefined +]; + +export declare function resolveEnvPrefix({ envPrefix }: UserConfig): string[]; + +export declare type ResolveFn = (id: string, importer?: string, aliasOnly?: boolean, ssr?: boolean) => Promise; + +export declare type ResolveModulePreloadDependenciesFn = (filename: string, deps: string[], context: { + hostId: string; + hostType: 'html' | 'js'; +}) => string[]; + +export declare interface ResolveOptions { + mainFields?: string[]; + /** + * @deprecated In future, `mainFields` should be used instead. + * @default true + */ + browserField?: boolean; + conditions?: string[]; + extensions?: string[]; + dedupe?: string[]; + preserveSymlinks?: boolean; +} + +export declare function resolvePackageData(id: string, basedir: string, preserveSymlinks?: boolean, packageCache?: PackageCache): PackageData | null; + +export declare function resolvePackageEntry(id: string, { dir, data, setResolvedCache, getResolvedCache }: PackageData, targetWeb: boolean, options: InternalResolveOptions): string | undefined; + +export declare type ResolverFunction = MapToFunction + +export declare interface ResolverObject { + buildStart?: PluginHooks['buildStart'] + resolveId: ResolverFunction +} + +export declare interface ResolveWorkerOptions extends PluginHookUtils { + format: 'es' | 'iife'; + plugins: Plugin_2[]; + rollupOptions: RollupOptions; +} + +/** + * https://github.com/rollup/plugins/blob/master/packages/commonjs/types/index.d.ts + * + * This source code is licensed under the MIT license found in the + * LICENSE file at + * https://github.com/rollup/plugins/blob/master/LICENSE + */ +export declare interface RollupCommonJSOptions { + /** + * A minimatch pattern, or array of patterns, which specifies the files in + * the build the plugin should operate on. By default, all files with + * extension `".cjs"` or those in `extensions` are included, but you can + * narrow this list by only including specific files. These files will be + * analyzed and transpiled if either the analysis does not find ES module + * specific statements or `transformMixedEsModules` is `true`. + * @default undefined + */ + include?: string | RegExp | readonly (string | RegExp)[] + /** + * A minimatch pattern, or array of patterns, which specifies the files in + * the build the plugin should _ignore_. By default, all files with + * extensions other than those in `extensions` or `".cjs"` are ignored, but you + * can exclude additional files. See also the `include` option. + * @default undefined + */ + exclude?: string | RegExp | readonly (string | RegExp)[] + /** + * For extensionless imports, search for extensions other than .js in the + * order specified. Note that you need to make sure that non-JavaScript files + * are transpiled by another plugin first. + * @default [ '.js' ] + */ + extensions?: ReadonlyArray + /** + * If true then uses of `global` won't be dealt with by this plugin + * @default false + */ + ignoreGlobal?: boolean + /** + * If false, skips source map generation for CommonJS modules. This will + * improve performance. + * @default true + */ + sourceMap?: boolean + /** + * Some `require` calls cannot be resolved statically to be translated to + * imports. + * When this option is set to `false`, the generated code will either + * directly throw an error when such a call is encountered or, when + * `dynamicRequireTargets` is used, when such a call cannot be resolved with a + * configured dynamic require target. + * Setting this option to `true` will instead leave the `require` call in the + * code or use it as a fallback for `dynamicRequireTargets`. + * @default false + */ + ignoreDynamicRequires?: boolean + /** + * Instructs the plugin whether to enable mixed module transformations. This + * is useful in scenarios with modules that contain a mix of ES `import` + * statements and CommonJS `require` expressions. Set to `true` if `require` + * calls should be transformed to imports in mixed modules, or `false` if the + * `require` expressions should survive the transformation. The latter can be + * important if the code contains environment detection, or you are coding + * for an environment with special treatment for `require` calls such as + * ElectronJS. See also the `ignore` option. + * @default false + */ + transformMixedEsModules?: boolean + /** + * By default, this plugin will try to hoist `require` statements as imports + * to the top of each file. While this works well for many code bases and + * allows for very efficient ESM output, it does not perfectly capture + * CommonJS semantics as the order of side effects like log statements may + * change. But it is especially problematic when there are circular `require` + * calls between CommonJS modules as those often rely on the lazy execution of + * nested `require` calls. + * + * Setting this option to `true` will wrap all CommonJS files in functions + * which are executed when they are required for the first time, preserving + * NodeJS semantics. Note that this can have an impact on the size and + * performance of the generated code. + * + * The default value of `"auto"` will only wrap CommonJS files when they are + * part of a CommonJS dependency cycle, e.g. an index file that is required by + * many of its dependencies. All other CommonJS files are hoisted. This is the + * recommended setting for most code bases. + * + * `false` will entirely prevent wrapping and hoist all files. This may still + * work depending on the nature of cyclic dependencies but will often cause + * problems. + * + * You can also provide a minimatch pattern, or array of patterns, to only + * specify a subset of files which should be wrapped in functions for proper + * `require` semantics. + * + * `"debug"` works like `"auto"` but after bundling, it will display a warning + * containing a list of ids that have been wrapped which can be used as + * minimatch pattern for fine-tuning. + * @default "auto" + */ + strictRequires?: boolean | string | RegExp | readonly (string | RegExp)[] + /** + * Sometimes you have to leave require statements unconverted. Pass an array + * containing the IDs or a `id => boolean` function. + * @default [] + */ + ignore?: ReadonlyArray | ((id: string) => boolean) + /** + * In most cases, where `require` calls are inside a `try-catch` clause, + * they should be left unconverted as it requires an optional dependency + * that may or may not be installed beside the rolled up package. + * Due to the conversion of `require` to a static `import` - the call is + * hoisted to the top of the file, outside of the `try-catch` clause. + * + * - `true`: All `require` calls inside a `try` will be left unconverted. + * - `false`: All `require` calls inside a `try` will be converted as if the + * `try-catch` clause is not there. + * - `remove`: Remove all `require` calls from inside any `try` block. + * - `string[]`: Pass an array containing the IDs to left unconverted. + * - `((id: string) => boolean|'remove')`: Pass a function that control + * individual IDs. + * + * @default false + */ + ignoreTryCatch?: + | boolean + | 'remove' + | ReadonlyArray + | ((id: string) => boolean | 'remove') + /** + * Controls how to render imports from external dependencies. By default, + * this plugin assumes that all external dependencies are CommonJS. This + * means they are rendered as default imports to be compatible with e.g. + * NodeJS where ES modules can only import a default export from a CommonJS + * dependency. + * + * If you set `esmExternals` to `true`, this plugins assumes that all + * external dependencies are ES modules and respect the + * `requireReturnsDefault` option. If that option is not set, they will be + * rendered as namespace imports. + * + * You can also supply an array of ids to be treated as ES modules, or a + * function that will be passed each external id to determine if it is an ES + * module. + * @default false + */ + esmExternals?: boolean | ReadonlyArray | ((id: string) => boolean) + /** + * Controls what is returned when requiring an ES module from a CommonJS file. + * When using the `esmExternals` option, this will also apply to external + * modules. By default, this plugin will render those imports as namespace + * imports i.e. + * + * ```js + * // input + * const foo = require('foo'); + * + * // output + * import * as foo from 'foo'; + * ``` + * + * However there are some situations where this may not be desired. + * For these situations, you can change Rollup's behaviour either globally or + * per module. To change it globally, set the `requireReturnsDefault` option + * to one of the following values: + * + * - `false`: This is the default, requiring an ES module returns its + * namespace. This is the only option that will also add a marker + * `__esModule: true` to the namespace to support interop patterns in + * CommonJS modules that are transpiled ES modules. + * - `"namespace"`: Like `false`, requiring an ES module returns its + * namespace, but the plugin does not add the `__esModule` marker and thus + * creates more efficient code. For external dependencies when using + * `esmExternals: true`, no additional interop code is generated. + * - `"auto"`: This is complementary to how `output.exports: "auto"` works in + * Rollup: If a module has a default export and no named exports, requiring + * that module returns the default export. In all other cases, the namespace + * is returned. For external dependencies when using `esmExternals: true`, a + * corresponding interop helper is added. + * - `"preferred"`: If a module has a default export, requiring that module + * always returns the default export, no matter whether additional named + * exports exist. This is similar to how previous versions of this plugin + * worked. Again for external dependencies when using `esmExternals: true`, + * an interop helper is added. + * - `true`: This will always try to return the default export on require + * without checking if it actually exists. This can throw at build time if + * there is no default export. This is how external dependencies are handled + * when `esmExternals` is not used. The advantage over the other options is + * that, like `false`, this does not add an interop helper for external + * dependencies, keeping the code lean. + * + * To change this for individual modules, you can supply a function for + * `requireReturnsDefault` instead. This function will then be called once for + * each required ES module or external dependency with the corresponding id + * and allows you to return different values for different modules. + * @default false + */ + requireReturnsDefault?: + | boolean + | 'auto' + | 'preferred' + | 'namespace' + | ((id: string) => boolean | 'auto' | 'preferred' | 'namespace') + + /** + * @default "auto" + */ + defaultIsModuleExports?: boolean | 'auto' | ((id: string) => boolean | 'auto') + /** + * Some modules contain dynamic `require` calls, or require modules that + * contain circular dependencies, which are not handled well by static + * imports. Including those modules as `dynamicRequireTargets` will simulate a + * CommonJS (NodeJS-like) environment for them with support for dynamic + * dependencies. It also enables `strictRequires` for those modules. + * + * Note: In extreme cases, this feature may result in some paths being + * rendered as absolute in the final bundle. The plugin tries to avoid + * exposing paths from the local machine, but if you are `dynamicRequirePaths` + * with paths that are far away from your project's folder, that may require + * replacing strings like `"/Users/John/Desktop/foo-project/"` -\> `"/"`. + */ + dynamicRequireTargets?: string | ReadonlyArray + /** + * To avoid long paths when using the `dynamicRequireTargets` option, you can use this option to specify a directory + * that is a common parent for all files that use dynamic require statements. Using a directory higher up such as `/` + * may lead to unnecessarily long paths in the generated code and may expose directory names on your machine like your + * home directory name. By default it uses the current working directory. + */ + dynamicRequireRoot?: string +} + +export declare interface RollupDynamicImportVarsOptions { + /** + * Files to include in this plugin (default all). + * @default [] + */ + include?: string | RegExp | (string | RegExp)[] + /** + * Files to exclude in this plugin (default none). + * @default [] + */ + exclude?: string | RegExp | (string | RegExp)[] + /** + * By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched. + * @default false + */ + warnOnError?: boolean +} + +export { rollupVersion } + +/** + * Search up for the nearest workspace root + */ +export declare function searchForWorkspaceRoot(current: string, root?: string): string; + +export declare function send(req: IncomingMessage, res: ServerResponse, content: string | Buffer, type: string, options: SendOptions): void; + +export declare interface SendOptions { + etag?: string; + cacheControl?: string; + headers?: OutgoingHttpHeaders; + map?: SourceMap | null; +} + +export declare type ServerHook = (this: void, server: ViteDevServer) => (() => void) | void | Promise<(() => void) | void>; + +export declare interface ServerOptions extends CommonServerOptions { + /** + * Configure HMR-specific options (port, host, path & protocol) + */ + hmr?: HmrOptions | boolean; + /** + * chokidar watch options + * https://github.com/paulmillr/chokidar#api + */ + watch?: WatchOptions; + /** + * Create Vite dev server to be used as a middleware in an existing server + */ + middlewareMode?: boolean | 'html' | 'ssr'; + /** + * Prepend this folder to http requests, for use when proxying vite as a subfolder + * Should start and end with the `/` character + */ + base?: string; + /** + * Options for files served via '/\@fs/'. + */ + fs?: FileSystemServeOptions; + /** + * Origin for the generated asset URLs. + * + * @example `http://127.0.0.1:8080` + */ + origin?: string; + /** + * Pre-transform known direct imports + * @default true + */ + preTransformRequests?: boolean; + /** + * Force dep pre-optimization regardless of whether deps have changed. + * + * @deprecated Use optimizeDeps.force instead, this option may be removed + * in a future minor version without following semver + */ + force?: boolean; +} + +export declare function sortUserPlugins(plugins: (Plugin_2 | Plugin_2[])[] | undefined): [Plugin_2[], Plugin_2[], Plugin_2[]]; + +export declare function splitVendorChunk(options?: { + cache?: SplitVendorChunkCache; +}): GetManualChunk; + +export declare class SplitVendorChunkCache { + cache: Map; + constructor(); + reset(): void; +} + +export declare function splitVendorChunkPlugin(): Plugin_2; + +export declare type SsrDepOptimizationOptions = DepOptimizationConfig; + +export declare type SSRFormat = 'esm' | 'cjs'; + +export declare interface SSROptions { + noExternal?: string | RegExp | (string | RegExp)[] | true; + external?: string[]; + /** + * Define the target for the ssr build. The browser field in package.json + * is ignored for node but used if webworker is the target + * Default: 'node' + */ + target?: SSRTarget; + /** + * Define the format for the ssr build. Since Vite v3 the SSR build generates ESM by default. + * `'cjs'` can be selected to generate a CJS build, but it isn't recommended. This option is + * left marked as experimental to give users more time to update to ESM. CJS builds requires + * complex externalization heuristics that aren't present in the ESM format. + * @experimental + */ + format?: SSRFormat; + /** + * Control over which dependencies are optimized during SSR and esbuild options + * During build: + * no external CJS dependencies are optimized by default + * During dev: + * explicit no external CJS dependencies are optimized by default + * @experimental + */ + optimizeDeps?: SsrDepOptimizationOptions; +} + +export declare type SSRTarget = 'node' | 'webworker'; + +export declare namespace Terser { + export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 + + export interface ParseOptions { + bare_returns?: boolean + /** @deprecated legacy option. Currently, all supported EcmaScript is valid to parse. */ + ecma?: ECMA + html5_comments?: boolean + shebang?: boolean + } + + export interface CompressOptions { + arguments?: boolean + arrows?: boolean + booleans_as_integers?: boolean + booleans?: boolean + collapse_vars?: boolean + comparisons?: boolean + computed_props?: boolean + conditionals?: boolean + dead_code?: boolean + defaults?: boolean + directives?: boolean + drop_console?: boolean + drop_debugger?: boolean + ecma?: ECMA + evaluate?: boolean + expression?: boolean + global_defs?: object + hoist_funs?: boolean + hoist_props?: boolean + hoist_vars?: boolean + ie8?: boolean + if_return?: boolean + inline?: boolean | InlineFunctions + join_vars?: boolean + keep_classnames?: boolean | RegExp + keep_fargs?: boolean + keep_fnames?: boolean | RegExp + keep_infinity?: boolean + loops?: boolean + module?: boolean + negate_iife?: boolean + passes?: number + properties?: boolean + pure_funcs?: string[] + pure_getters?: boolean | 'strict' + reduce_funcs?: boolean + reduce_vars?: boolean + sequences?: boolean | number + side_effects?: boolean + switches?: boolean + toplevel?: boolean + top_retain?: null | string | string[] | RegExp + typeofs?: boolean + unsafe_arrows?: boolean + unsafe?: boolean + unsafe_comps?: boolean + unsafe_Function?: boolean + unsafe_math?: boolean + unsafe_symbols?: boolean + unsafe_methods?: boolean + unsafe_proto?: boolean + unsafe_regexp?: boolean + unsafe_undefined?: boolean + unused?: boolean + } + + export enum InlineFunctions { + Disabled = 0, + SimpleFunctions = 1, + WithArguments = 2, + WithArgumentsAndVariables = 3 + } + + export interface MangleOptions { + eval?: boolean + keep_classnames?: boolean | RegExp + keep_fnames?: boolean | RegExp + module?: boolean + nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler + properties?: boolean | ManglePropertiesOptions + reserved?: string[] + safari10?: boolean + toplevel?: boolean + } + + /** + * An identifier mangler for which the output is invariant with respect to the source code. + */ + export interface SimpleIdentifierMangler { + /** + * Obtains the nth most favored (usually shortest) identifier to rename a variable to. + * The mangler will increment n and retry until the return value is not in use in scope, and is not a reserved word. + * This function is expected to be stable; Evaluating get(n) === get(n) should always return true. + * @param n - The ordinal of the identifier. + */ + get(n: number): string + } + + /** + * An identifier mangler that leverages character frequency analysis to determine identifier precedence. + */ + export interface WeightedIdentifierMangler extends SimpleIdentifierMangler { + /** + * Modifies the internal weighting of the input characters by the specified delta. + * Will be invoked on the entire printed AST, and then deduct mangleable identifiers. + * @param chars - The characters to modify the weighting of. + * @param delta - The numeric weight to add to the characters. + */ + consider(chars: string, delta: number): number + /** + * Resets character weights. + */ + reset(): void + /** + * Sorts identifiers by character frequency, in preparation for calls to get(n). + */ + sort(): void + } + + export interface ManglePropertiesOptions { + builtins?: boolean + debug?: boolean + keep_quoted?: boolean | 'strict' + nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler + regex?: RegExp | string + reserved?: string[] + } + + export interface FormatOptions { + ascii_only?: boolean + /** @deprecated Not implemented anymore */ + beautify?: boolean + braces?: boolean + comments?: + | boolean + | 'all' + | 'some' + | RegExp + | (( + node: any, + comment: { + value: string + type: 'comment1' | 'comment2' | 'comment3' | 'comment4' + pos: number + line: number + col: number + } + ) => boolean) + ecma?: ECMA + ie8?: boolean + keep_numbers?: boolean + indent_level?: number + indent_start?: number + inline_script?: boolean + keep_quoted_props?: boolean + max_line_len?: number | false + preamble?: string + preserve_annotations?: boolean + quote_keys?: boolean + quote_style?: OutputQuoteStyle + safari10?: boolean + semicolons?: boolean + shebang?: boolean + shorthand?: boolean + source_map?: SourceMapOptions + webkit?: boolean + width?: number + wrap_iife?: boolean + wrap_func_args?: boolean + } + + export enum OutputQuoteStyle { + PreferDouble = 0, + AlwaysSingle = 1, + AlwaysDouble = 2, + AlwaysOriginal = 3 + } + + export interface MinifyOptions { + compress?: boolean | CompressOptions + ecma?: ECMA + enclose?: boolean | string + ie8?: boolean + keep_classnames?: boolean | RegExp + keep_fnames?: boolean | RegExp + mangle?: boolean | MangleOptions + module?: boolean + nameCache?: object + format?: FormatOptions + /** @deprecated deprecated */ + output?: FormatOptions + parse?: ParseOptions + safari10?: boolean + sourceMap?: boolean | SourceMapOptions + toplevel?: boolean + } + + export interface MinifyOutput { + code?: string + map?: object | string + decoded_map?: object | null + } + + export interface SourceMapOptions { + /** Source map object, 'inline' or source map file content */ + content?: object | string + includeSources?: boolean + filename?: string + root?: string + url?: string | 'inline' + } +} + +export declare interface TransformOptions { + ssr?: boolean; + html?: boolean; +} + +export declare interface TransformResult { + code: string; + map: SourceMap | null; + etag?: string; + deps?: string[]; + dynamicDeps?: string[]; +} + +export declare function transformWithEsbuild(code: string, filename: string, options?: EsbuildTransformOptions, inMap?: object): Promise; + +export { Update } + +export { UpdatePayload } + +export declare interface UserConfig { + /** + * Project root directory. Can be an absolute path, or a path relative from + * the location of the config file itself. + * @default process.cwd() + */ + root?: string; + /** + * Base public path when served in development or production. + * @default '/' + */ + base?: string; + /** + * Directory to serve as plain static assets. Files in this directory are + * served and copied to build dist dir as-is without transform. The value + * can be either an absolute file system path or a path relative to project root. + * + * Set to `false` or an empty string to disable copied static assets to build dist dir. + * @default 'public' + */ + publicDir?: string | false; + /** + * Directory to save cache files. Files in this directory are pre-bundled + * deps or some other cache files that generated by vite, which can improve + * the performance. You can use `--force` flag or manually delete the directory + * to regenerate the cache files. The value can be either an absolute file + * system path or a path relative to project root. + * Default to `.vite` when no `package.json` is detected. + * @default 'node_modules/.vite' + */ + cacheDir?: string; + /** + * Explicitly set a mode to run in. This will override the default mode for + * each command, and can be overridden by the command line --mode option. + */ + mode?: string; + /** + * Define global variable replacements. + * Entries will be defined on `window` during dev and replaced during build. + */ + define?: Record; + /** + * Array of vite plugins to use. + */ + plugins?: PluginOption[]; + /** + * Configure resolver + */ + resolve?: ResolveOptions & { + alias?: AliasOptions; + }; + /** + * CSS related options (preprocessors and CSS modules) + */ + css?: CSSOptions; + /** + * JSON loading options + */ + json?: JsonOptions; + /** + * Transform options to pass to esbuild. + * Or set to `false` to disable esbuild. + */ + esbuild?: ESBuildOptions | false; + /** + * Specify additional picomatch patterns to be treated as static assets. + */ + assetsInclude?: string | RegExp | (string | RegExp)[]; + /** + * Server specific options, e.g. host, port, https... + */ + server?: ServerOptions; + /** + * Build specific options + */ + build?: BuildOptions; + /** + * Preview specific options, e.g. host, port, https... + */ + preview?: PreviewOptions; + /** + * Dep optimization options + */ + optimizeDeps?: DepOptimizationOptions; + /** + * SSR specific options + */ + ssr?: SSROptions; + /** + * Experimental features + * + * Features under this field could change in the future and might NOT follow semver. + * Please be careful and always pin Vite's version when using them. + * @experimental + */ + experimental?: ExperimentalOptions; + /** + * Legacy options + * + * Features under this field only follow semver for patches, they could be removed in a + * future minor version. Please always pin Vite's version to a minor when using them. + */ + legacy?: LegacyOptions; + /** + * Log level. + * Default: 'info' + */ + logLevel?: LogLevel; + /** + * Custom logger. + */ + customLogger?: Logger; + /** + * Default: true + */ + clearScreen?: boolean; + /** + * Environment files directory. Can be an absolute path, or a path relative from + * the location of the config file itself. + * @default root + */ + envDir?: string; + /** + * Env variables starts with `envPrefix` will be exposed to your client source code via import.meta.env. + * @default 'VITE_' + */ + envPrefix?: string | string[]; + /** + * Worker bundle options + */ + worker?: { + /** + * Output format for worker bundle + * @default 'iife' + */ + format?: 'es' | 'iife'; + /** + * Vite plugins that apply to worker bundle + */ + plugins?: PluginOption[]; + /** + * Rollup options to build worker bundle + */ + rollupOptions?: Omit; + }; + /** + * Whether your application is a Single Page Application (SPA), + * a Multi-Page Application (MPA), or Custom Application (SSR + * and frameworks with custom HTML handling) + * @default 'spa' + */ + appType?: AppType; +} + +export declare type UserConfigExport = UserConfig | Promise | UserConfigFn; + +export declare type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise; + +export declare const version: string; + +export declare interface ViteDevServer { + /** + * The resolved vite config object + */ + config: ResolvedConfig; + /** + * A connect app instance. + * - Can be used to attach custom middlewares to the dev server. + * - Can also be used as the handler function of a custom http server + * or as a middleware in any connect-style Node.js frameworks + * + * https://github.com/senchalabs/connect#use-middleware + */ + middlewares: Connect.Server; + /** + * native Node http server instance + * will be null in middleware mode + */ + httpServer: http.Server | null; + /** + * chokidar watcher instance + * https://github.com/paulmillr/chokidar#api + */ + watcher: FSWatcher; + /** + * web socket server with `send(payload)` method + */ + ws: WebSocketServer; + /** + * Rollup plugin container that can run plugin hooks on a given file + */ + pluginContainer: PluginContainer; + /** + * Module graph that tracks the import relationships, url to file mapping + * and hmr state. + */ + moduleGraph: ModuleGraph; + /** + * The resolved urls Vite prints on the CLI. null in middleware mode or + * before `server.listen` is called. + */ + resolvedUrls: ResolvedServerUrls | null; + /** + * Programmatically resolve, load and transform a URL and get the result + * without going through the http request pipeline. + */ + transformRequest(url: string, options?: TransformOptions): Promise; + /** + * Apply vite built-in HTML transforms and any plugin HTML transforms. + */ + transformIndexHtml(url: string, html: string, originalUrl?: string): Promise; + /** + * Transform module code into SSR format. + */ + ssrTransform(code: string, inMap: SourceMap | null, url: string, originalCode?: string): Promise; + /** + * Load a given URL as an instantiated module for SSR. + */ + ssrLoadModule(url: string, opts?: { + fixStacktrace?: boolean; + }): Promise>; + /** + * Returns a fixed version of the given stack + */ + ssrRewriteStacktrace(stack: string): string; + /** + * Mutates the given SSR error by rewriting the stacktrace + */ + ssrFixStacktrace(e: Error): void; + /** + * Triggers HMR for a module in the module graph. You can use the `server.moduleGraph` + * API to retrieve the module to be reloaded. If `hmr` is false, this is a no-op. + */ + reloadModule(module: ModuleNode): Promise; + /** + * Start the server. + */ + listen(port?: number, isRestart?: boolean): Promise; + /** + * Stop the server. + */ + close(): Promise; + /** + * Print server urls + */ + printUrls(): void; + /** + * Restart the server. + * + * @param forceOptimize - force the optimizer to re-bundle, same as --force cli flag + */ + restart(forceOptimize?: boolean): Promise; + /* Excluded from this release type: _importGlobMap */ + /* Excluded from this release type: _ssrExternals */ + /* Excluded from this release type: _restartPromise */ + /* Excluded from this release type: _forceOptimizeOnRestart */ + /* Excluded from this release type: _pendingRequests */ + /* Excluded from this release type: _fsDenyGlob */ +} + +export declare interface WatchOptions { + /** + * Indicates whether the process should continue to run as long as files are being watched. If + * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, + * even if the process continues to run. + */ + persistent?: boolean + + /** + * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to + * be ignored. The whole relative or absolute path is tested, not just filename. If a function + * with two arguments is provided, it gets called twice per path - once with a single argument + * (the path), second time with two arguments (the path and the + * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). + */ + ignored?: Matcher + + /** + * If set to `false` then `add`/`addDir` events are also emitted for matching paths while + * instantiating the watching as chokidar discovers these file paths (before the `ready` event). + */ + ignoreInitial?: boolean + + /** + * When `false`, only the symlinks themselves will be watched for changes instead of following + * the link references and bubbling events through the link's path. + */ + followSymlinks?: boolean + + /** + * The base directory from which watch `paths` are to be derived. Paths emitted with events will + * be relative to this. + */ + cwd?: string + + /** + * If set to true then the strings passed to .watch() and .add() are treated as literal path + * names, even if they look like globs. + * + * @default false + */ + disableGlobbing?: boolean + + /** + * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU + * utilization, consider setting this to `false`. It is typically necessary to **set this to + * `true` to successfully watch files over a network**, and it may be necessary to successfully + * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides + * the `useFsEvents` default. + */ + usePolling?: boolean + + /** + * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly + * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on + * OS X, `usePolling: true` becomes the default. + */ + useFsEvents?: boolean + + /** + * If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that + * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is + * provided even in cases where it wasn't already available from the underlying watch events. + */ + alwaysStat?: boolean + + /** + * If set, limits how many levels of subdirectories will be traversed. + */ + depth?: number + + /** + * Interval of file system polling. + */ + interval?: number + + /** + * Interval of file system polling for binary files. ([see list of binary extensions](https://gi + * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) + */ + binaryInterval?: number + + /** + * Indicates whether to watch files that don't have read permissions if possible. If watching + * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed + * silently. + */ + ignorePermissionErrors?: boolean + + /** + * `true` if `useFsEvents` and `usePolling` are `false`. Automatically filters out artifacts + * that occur when using editors that use "atomic writes" instead of writing directly to the + * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` + * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, + * you can override it by setting `atomic` to a custom value, in milliseconds. + */ + atomic?: boolean | number + + /** + * can be set to an object in order to adjust timing params: + */ + awaitWriteFinish?: AwaitWriteFinishOptions | boolean +} + +declare class WebSocket_2 extends EventEmitter { + /** The connection is not yet open. */ + static readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + static readonly OPEN: 1 + /** The connection is in the process of closing. */ + static readonly CLOSING: 2 + /** The connection is closed. */ + static readonly CLOSED: 3 + + binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' + readonly bufferedAmount: number + readonly extensions: string + /** Indicates whether the websocket is paused */ + readonly isPaused: boolean + readonly protocol: string + /** The current state of the connection */ + readonly readyState: + | typeof WebSocket_2.CONNECTING + | typeof WebSocket_2.OPEN + | typeof WebSocket_2.CLOSING + | typeof WebSocket_2.CLOSED + readonly url: string + + /** The connection is not yet open. */ + readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + readonly OPEN: 1 + /** The connection is in the process of closing. */ + readonly CLOSING: 2 + /** The connection is closed. */ + readonly CLOSED: 3 + + onopen: ((event: WebSocket_2.Event) => void) | null + onerror: ((event: WebSocket_2.ErrorEvent) => void) | null + onclose: ((event: WebSocket_2.CloseEvent) => void) | null + onmessage: ((event: WebSocket_2.MessageEvent) => void) | null + + constructor(address: null) + constructor( + address: string | URL_2, + options?: WebSocket_2.ClientOptions | ClientRequestArgs + ) + constructor( + address: string | URL_2, + protocols?: string | string[], + options?: WebSocket_2.ClientOptions | ClientRequestArgs + ) + + close(code?: number, data?: string | Buffer): void + ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void + pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void + send(data: any, cb?: (err?: Error) => void): void + send( + data: any, + options: { + mask?: boolean | undefined + binary?: boolean | undefined + compress?: boolean | undefined + fin?: boolean | undefined + }, + cb?: (err?: Error) => void + ): void + terminate(): void + + /** + * Pause the websocket causing it to stop emitting events. Some events can still be + * emitted after this is called, until all buffered data is consumed. This method + * is a noop if the ready state is `CONNECTING` or `CLOSED`. + */ + pause(): void + /** + * Make a paused socket resume emitting events. This method is a noop if the ready + * state is `CONNECTING` or `CLOSED`. + */ + resume(): void + + // HTML5 WebSocket events + addEventListener( + method: 'message', + cb: (event: WebSocket_2.MessageEvent) => void, + options?: WebSocket_2.EventListenerOptions + ): void + addEventListener( + method: 'close', + cb: (event: WebSocket_2.CloseEvent) => void, + options?: WebSocket_2.EventListenerOptions + ): void + addEventListener( + method: 'error', + cb: (event: WebSocket_2.ErrorEvent) => void, + options?: WebSocket_2.EventListenerOptions + ): void + addEventListener( + method: 'open', + cb: (event: WebSocket_2.Event) => void, + options?: WebSocket_2.EventListenerOptions + ): void + + removeEventListener( + method: 'message', + cb: (event: WebSocket_2.MessageEvent) => void + ): void + removeEventListener( + method: 'close', + cb: (event: WebSocket_2.CloseEvent) => void + ): void + removeEventListener( + method: 'error', + cb: (event: WebSocket_2.ErrorEvent) => void + ): void + removeEventListener( + method: 'open', + cb: (event: WebSocket_2.Event) => void + ): void + + // Events + on( + event: 'close', + listener: (this: WebSocket_2, code: number, reason: Buffer) => void + ): this + on(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this + on( + event: 'upgrade', + listener: (this: WebSocket_2, request: IncomingMessage) => void + ): this + on( + event: 'message', + listener: ( + this: WebSocket_2, + data: WebSocket_2.RawData, + isBinary: boolean + ) => void + ): this + on(event: 'open', listener: (this: WebSocket_2) => void): this + on( + event: 'ping' | 'pong', + listener: (this: WebSocket_2, data: Buffer) => void + ): this + on( + event: 'unexpected-response', + listener: ( + this: WebSocket_2, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + on( + event: string | symbol, + listener: (this: WebSocket_2, ...args: any[]) => void + ): this + + once( + event: 'close', + listener: (this: WebSocket_2, code: number, reason: Buffer) => void + ): this + once(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this + once( + event: 'upgrade', + listener: (this: WebSocket_2, request: IncomingMessage) => void + ): this + once( + event: 'message', + listener: ( + this: WebSocket_2, + data: WebSocket_2.RawData, + isBinary: boolean + ) => void + ): this + once(event: 'open', listener: (this: WebSocket_2) => void): this + once( + event: 'ping' | 'pong', + listener: (this: WebSocket_2, data: Buffer) => void + ): this + once( + event: 'unexpected-response', + listener: ( + this: WebSocket_2, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + once( + event: string | symbol, + listener: (this: WebSocket_2, ...args: any[]) => void + ): this + + off( + event: 'close', + listener: (this: WebSocket_2, code: number, reason: Buffer) => void + ): this + off(event: 'error', listener: (this: WebSocket_2, err: Error) => void): this + off( + event: 'upgrade', + listener: (this: WebSocket_2, request: IncomingMessage) => void + ): this + off( + event: 'message', + listener: ( + this: WebSocket_2, + data: WebSocket_2.RawData, + isBinary: boolean + ) => void + ): this + off(event: 'open', listener: (this: WebSocket_2) => void): this + off( + event: 'ping' | 'pong', + listener: (this: WebSocket_2, data: Buffer) => void + ): this + off( + event: 'unexpected-response', + listener: ( + this: WebSocket_2, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + off( + event: string | symbol, + listener: (this: WebSocket_2, ...args: any[]) => void + ): this + + addListener( + event: 'close', + listener: (code: number, reason: Buffer) => void + ): this + addListener(event: 'error', listener: (err: Error) => void): this + addListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + addListener( + event: 'message', + listener: (data: WebSocket_2.RawData, isBinary: boolean) => void + ): this + addListener(event: 'open', listener: () => void): this + addListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this + addListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + addListener(event: string | symbol, listener: (...args: any[]) => void): this + + removeListener( + event: 'close', + listener: (code: number, reason: Buffer) => void + ): this + removeListener(event: 'error', listener: (err: Error) => void): this + removeListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + removeListener( + event: 'message', + listener: (data: WebSocket_2.RawData, isBinary: boolean) => void + ): this + removeListener(event: 'open', listener: () => void): this + removeListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this + removeListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this +} + +declare namespace WebSocket_2 { + /** + * Data represents the raw message payload received over the WebSocket. + */ + type RawData = Buffer | ArrayBuffer | Buffer[] + + /** + * Data represents the message payload received over the WebSocket. + */ + type Data = string | Buffer | ArrayBuffer | Buffer[] + + /** + * CertMeta represents the accepted types for certificate & key data. + */ + type CertMeta = string | string[] | Buffer | Buffer[] + + /** + * VerifyClientCallbackSync is a synchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackSync = (info: { + origin: string + secure: boolean + req: IncomingMessage + }) => boolean + + /** + * VerifyClientCallbackAsync is an asynchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackAsync = ( + info: { origin: string; secure: boolean; req: IncomingMessage }, + callback: ( + res: boolean, + code?: number, + message?: string, + headers?: OutgoingHttpHeaders + ) => void + ) => void + + interface ClientOptions extends SecureContextOptions { + protocol?: string | undefined + followRedirects?: boolean | undefined + generateMask?(mask: Buffer): void + handshakeTimeout?: number | undefined + maxRedirects?: number | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + localAddress?: string | undefined + protocolVersion?: number | undefined + headers?: { [key: string]: string } | undefined + origin?: string | undefined + agent?: Agent | undefined + host?: string | undefined + family?: number | undefined + checkServerIdentity?(servername: string, cert: CertMeta): boolean + rejectUnauthorized?: boolean | undefined + maxPayload?: number | undefined + skipUTF8Validation?: boolean | undefined + } + + interface PerMessageDeflateOptions { + serverNoContextTakeover?: boolean | undefined + clientNoContextTakeover?: boolean | undefined + serverMaxWindowBits?: number | undefined + clientMaxWindowBits?: number | undefined + zlibDeflateOptions?: + | { + flush?: number | undefined + finishFlush?: number | undefined + chunkSize?: number | undefined + windowBits?: number | undefined + level?: number | undefined + memLevel?: number | undefined + strategy?: number | undefined + dictionary?: Buffer | Buffer[] | DataView | undefined + info?: boolean | undefined + } + | undefined + zlibInflateOptions?: ZlibOptions | undefined + threshold?: number | undefined + concurrencyLimit?: number | undefined + } + + interface Event { + type: string + target: WebSocket + } + + interface ErrorEvent { + error: any + message: string + type: string + target: WebSocket + } + + interface CloseEvent { + wasClean: boolean + code: number + reason: string + type: string + target: WebSocket + } + + interface MessageEvent { + data: Data + type: string + target: WebSocket + } + + interface EventListenerOptions { + once?: boolean | undefined + } + + interface ServerOptions { + host?: string | undefined + port?: number | undefined + backlog?: number | undefined + server?: Server | Server_2 | undefined + verifyClient?: + | VerifyClientCallbackAsync + | VerifyClientCallbackSync + | undefined + handleProtocols?: ( + protocols: Set, + request: IncomingMessage + ) => string | false + path?: string | undefined + noServer?: boolean | undefined + clientTracking?: boolean | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + maxPayload?: number | undefined + skipUTF8Validation?: boolean | undefined + WebSocket?: typeof WebSocket.WebSocket | undefined + } + + interface AddressInfo { + address: string + family: string + port: number + } + + // WebSocket Server + class Server extends EventEmitter { + options: ServerOptions + path: string + clients: Set + + constructor(options?: ServerOptions, callback?: () => void) + + address(): AddressInfo | string + close(cb?: (err?: Error) => void): void + handleUpgrade( + request: IncomingMessage, + socket: Duplex, + upgradeHead: Buffer, + callback: (client: T, request: IncomingMessage) => void + ): void + shouldHandle(request: IncomingMessage): boolean | Promise + + // Events + on( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + on(event: 'error', cb: (this: Server, error: Error) => void): this + on( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + on(event: 'close' | 'listening', cb: (this: Server) => void): this + on( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + once( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + once(event: 'error', cb: (this: Server, error: Error) => void): this + once( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + once(event: 'close' | 'listening', cb: (this: Server) => void): this + once( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + off( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + off(event: 'error', cb: (this: Server, error: Error) => void): this + off( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + off(event: 'close' | 'listening', cb: (this: Server) => void): this + off( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + addListener( + event: 'connection', + cb: (client: T, request: IncomingMessage) => void + ): this + addListener(event: 'error', cb: (err: Error) => void): this + addListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + addListener(event: 'close' | 'listening', cb: () => void): this + addListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + + removeListener(event: 'connection', cb: (client: T) => void): this + removeListener(event: 'error', cb: (err: Error) => void): this + removeListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + removeListener(event: 'close' | 'listening', cb: () => void): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + } + + const WebSocketServer: typeof Server + interface WebSocketServer extends Server {} // tslint:disable-line no-empty-interface + const WebSocket: typeof WebSocketAlias + interface WebSocket extends WebSocketAlias {} // tslint:disable-line no-empty-interface + + // WebSocket stream + function createWebSocketStream( + websocket: WebSocket, + options?: DuplexOptions + ): Duplex +} +export { WebSocket_2 as WebSocket } + +export declare const WebSocketAlias: typeof WebSocket_2; + +export declare interface WebSocketAlias extends WebSocket_2 {} + +export declare interface WebSocketClient { + /** + * Send event to the client + */ + send(payload: HMRPayload): void; + /** + * Send custom event + */ + send(event: string, payload?: CustomPayload['data']): void; + /** + * The raw WebSocket instance + * @advanced + */ + socket: WebSocket_2; +} + +export declare type WebSocketCustomListener = (data: T, client: WebSocketClient) => void; + +export declare interface WebSocketServer { + /** + * Get all connected clients. + */ + clients: Set; + /** + * Broadcast events to all clients + */ + send(payload: HMRPayload): void; + /** + * Send custom event + */ + send(event: T, payload?: InferCustomEventPayload): void; + /** + * Disconnect all clients and terminate the server. + */ + close(): Promise; + /** + * Handle custom event emitted by `import.meta.hot.send` + */ + on: WebSocket_2.Server['on'] & { + (event: T, listener: WebSocketCustomListener>): void; + }; + /** + * Unregister event listener. + */ + off: WebSocket_2.Server['off'] & { + (event: string, listener: Function): void; + }; +} + +export { } diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index c0099f5..d4671a1 100644 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -be0c7dc3573b2470ab6a8ec4c48845b6 \ No newline at end of file +5fbf12469d224a93954efecb5886e8a6 \ No newline at end of file diff --git a/frontend/src/style.css b/frontend/src/style.css index f4f5148..dd03273 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -10,6 +10,7 @@ --border-color: rgba(75, 85, 99, 0.4); --success-color: #10b981; --glass-border: rgba(255, 255, 255, 0.1); + color-scheme: dark; } body { @@ -63,6 +64,13 @@ label { margin-bottom: 10px; } +input[type="checkbox"] { + accent-color: var(--primary-color); + width: 18px; + height: 18px; + cursor: pointer; +} + input[type="text"], input[type="password"], select, @@ -78,6 +86,21 @@ textarea { box-sizing: border-box; } +select { + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 14px center; + background-size: 16px; + padding-right: 40px; +} + +select option { + background: var(--bg-color); + color: white; +} + textarea { resize: vertical; min-height: 80px; diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts old mode 100644 new mode 100755 index eb3ca65..052c502 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -1,14 +1,37 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +import {config} from '../models'; -export function ClearHistory(): Promise; +export function CheckOnline():Promise; -export function GetMicrophones(): Promise>; +export function ClearHistory():Promise; -export function GetSettings(): Promise<{ [key: string]: any }>; +export function GetAvailableWhisperModels():Promise>>; -export function Greet(arg1: string): Promise; +export function GetConfig():Promise; -export function SaveSettings(arg1: { [key: string]: any }): Promise; +export function GetMicrophones():Promise>>; -export function ToggleStartup(arg1: boolean): Promise; +export function GetSettings():Promise>; + +export function GetWhisperInfo():Promise>; + +export function Greet(arg1:string):Promise; + +export function InstallWhisper(arg1:string):Promise; + +export function IsWhisperInstalled():Promise; + +export function Quit():Promise; + +export function SaveSettings(arg1:Record):Promise; + +export function ShowSettings():Promise; + +export function StartRecording():Promise; + +export function StopRecording():Promise; + +export function ToggleStartup(arg1:boolean):Promise; + +export function UninstallWhisper():Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js old mode 100644 new mode 100755 index 0f7b259..fe9679c --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -1,50 +1,71 @@ // @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function ClearHistory() { - return window['go']['main']['App']['ClearHistory'](); -} - -export function GetMicrophones() { - return window['go']['main']['App']['GetMicrophones'](); -} - -export function GetSettings() { - return window['go']['main']['App']['GetSettings'](); -} - -export function Greet(arg1) { - return window['go']['main']['App']['Greet'](arg1); -} - -export function SaveSettings(arg1) { - return window['go']['main']['App']['SaveSettings'](arg1); -} - -export function ToggleStartup(arg1) { - return window['go']['main']['App']['ToggleStartup'](arg1); -} - export function CheckOnline() { - return window['go']['main']['App']['CheckOnline'](); + return window['go']['main']['App']['CheckOnline'](); } -export function IsWhisperInstalled() { - return window['go']['main']['App']['IsWhisperInstalled'](); -} - -export function GetWhisperInfo() { - return window['go']['main']['App']['GetWhisperInfo'](); -} - -export function InstallWhisper(arg1) { - return window['go']['main']['App']['InstallWhisper'](arg1); -} - -export function UninstallWhisper() { - return window['go']['main']['App']['UninstallWhisper'](); +export function ClearHistory() { + return window['go']['main']['App']['ClearHistory'](); } export function GetAvailableWhisperModels() { - return window['go']['main']['App']['GetAvailableWhisperModels'](); + return window['go']['main']['App']['GetAvailableWhisperModels'](); +} + +export function GetConfig() { + return window['go']['main']['App']['GetConfig'](); +} + +export function GetMicrophones() { + return window['go']['main']['App']['GetMicrophones'](); +} + +export function GetSettings() { + return window['go']['main']['App']['GetSettings'](); +} + +export function GetWhisperInfo() { + return window['go']['main']['App']['GetWhisperInfo'](); +} + +export function Greet(arg1) { + return window['go']['main']['App']['Greet'](arg1); +} + +export function InstallWhisper(arg1) { + return window['go']['main']['App']['InstallWhisper'](arg1); +} + +export function IsWhisperInstalled() { + return window['go']['main']['App']['IsWhisperInstalled'](); +} + +export function Quit() { + return window['go']['main']['App']['Quit'](); +} + +export function SaveSettings(arg1) { + return window['go']['main']['App']['SaveSettings'](arg1); +} + +export function ShowSettings() { + return window['go']['main']['App']['ShowSettings'](); +} + +export function StartRecording() { + return window['go']['main']['App']['StartRecording'](); +} + +export function StopRecording() { + return window['go']['main']['App']['StopRecording'](); +} + +export function ToggleStartup(arg1) { + return window['go']['main']['App']['ToggleStartup'](arg1); +} + +export function UninstallWhisper() { + return window['go']['main']['App']['UninstallWhisper'](); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts new file mode 100755 index 0000000..a14ca1a --- /dev/null +++ b/frontend/wailsjs/go/models.ts @@ -0,0 +1,63 @@ +export namespace config { + + export class HistoryItem { + text: string; + timestamp: string; + + static createFrom(source: any = {}) { + return new HistoryItem(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.text = source["text"]; + this.timestamp = source["timestamp"]; + } + } + export class Config { + api_key: string; + shortcut: string; + whisper_model: string; + ai_model: string; + ai_prompt: string; + language: string; + microphone_device?: number; + history: HistoryItem[]; + + static createFrom(source: any = {}) { + return new Config(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.api_key = source["api_key"]; + this.shortcut = source["shortcut"]; + this.whisper_model = source["whisper_model"]; + this.ai_model = source["ai_model"]; + this.ai_prompt = source["ai_prompt"]; + this.language = source["language"]; + this.microphone_device = source["microphone_device"]; + this.history = this.convertValues(source["history"], HistoryItem); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + +} + diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts index 4445dac..3bbea84 100644 --- a/frontend/wailsjs/runtime/runtime.d.ts +++ b/frontend/wailsjs/runtime/runtime.d.ts @@ -246,4 +246,85 @@ export function OnFileDropOff() :void export function CanResolveFilePaths(): boolean; // Resolves file paths for an array of files -export function ResolveFilePaths(files: File[]): void \ No newline at end of file +export function ResolveFilePaths(files: File[]): void + +// Notification types +export interface NotificationOptions { + id: string; + title: string; + subtitle?: string; // macOS and Linux only + body?: string; + categoryId?: string; + data?: { [key: string]: any }; +} + +export interface NotificationAction { + id?: string; + title?: string; + destructive?: boolean; // macOS-specific +} + +export interface NotificationCategory { + id?: string; + actions?: NotificationAction[]; + hasReplyField?: boolean; + replyPlaceholder?: string; + replyButtonTitle?: string; +} + +// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications) +// Initializes the notification service for the application. +// This must be called before sending any notifications. +export function InitializeNotifications(): Promise; + +// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications) +// Cleans up notification resources and releases any held connections. +export function CleanupNotifications(): Promise; + +// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable) +// Checks if notifications are available on the current platform. +export function IsNotificationAvailable(): Promise; + +// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization) +// Requests notification authorization from the user (macOS only). +export function RequestNotificationAuthorization(): Promise; + +// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization) +// Checks the current notification authorization status (macOS only). +export function CheckNotificationAuthorization(): Promise; + +// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification) +// Sends a basic notification with the given options. +export function SendNotification(options: NotificationOptions): Promise; + +// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions) +// Sends a notification with action buttons. Requires a registered category. +export function SendNotificationWithActions(options: NotificationOptions): Promise; + +// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory) +// Registers a notification category that can be used with SendNotificationWithActions. +export function RegisterNotificationCategory(category: NotificationCategory): Promise; + +// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory) +// Removes a previously registered notification category. +export function RemoveNotificationCategory(categoryId: string): Promise; + +// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications) +// Removes all pending notifications from the notification center. +export function RemoveAllPendingNotifications(): Promise; + +// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification) +// Removes a specific pending notification by its identifier. +export function RemovePendingNotification(identifier: string): Promise; + +// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications) +// Removes all delivered notifications from the notification center. +export function RemoveAllDeliveredNotifications(): Promise; + +// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification) +// Removes a specific delivered notification by its identifier. +export function RemoveDeliveredNotification(identifier: string): Promise; + +// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification) +// Removes a notification by its identifier (cross-platform convenience function). +export function RemoveNotification(identifier: string): Promise; \ No newline at end of file diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/wailsjs/runtime/runtime.js index 7cb89d7..556621e 100644 --- a/frontend/wailsjs/runtime/runtime.js +++ b/frontend/wailsjs/runtime/runtime.js @@ -239,4 +239,60 @@ export function CanResolveFilePaths() { export function ResolveFilePaths(files) { return window.runtime.ResolveFilePaths(files); +} + +export function InitializeNotifications() { + return window.runtime.InitializeNotifications(); +} + +export function CleanupNotifications() { + return window.runtime.CleanupNotifications(); +} + +export function IsNotificationAvailable() { + return window.runtime.IsNotificationAvailable(); +} + +export function RequestNotificationAuthorization() { + return window.runtime.RequestNotificationAuthorization(); +} + +export function CheckNotificationAuthorization() { + return window.runtime.CheckNotificationAuthorization(); +} + +export function SendNotification(options) { + return window.runtime.SendNotification(options); +} + +export function SendNotificationWithActions(options) { + return window.runtime.SendNotificationWithActions(options); +} + +export function RegisterNotificationCategory(category) { + return window.runtime.RegisterNotificationCategory(category); +} + +export function RemoveNotificationCategory(categoryId) { + return window.runtime.RemoveNotificationCategory(categoryId); +} + +export function RemoveAllPendingNotifications() { + return window.runtime.RemoveAllPendingNotifications(); +} + +export function RemovePendingNotification(identifier) { + return window.runtime.RemovePendingNotification(identifier); +} + +export function RemoveAllDeliveredNotifications() { + return window.runtime.RemoveAllDeliveredNotifications(); +} + +export function RemoveDeliveredNotification(identifier) { + return window.runtime.RemoveDeliveredNotification(identifier); +} + +export function RemoveNotification(identifier) { + return window.runtime.RemoveNotification(identifier); } \ No newline at end of file diff --git a/go.mod b/go.mod index beee4a7..c4a92f2 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,8 @@ require ( github.com/gen2brain/malgo v0.11.24 github.com/getlantern/systray v1.2.2 github.com/go-vgo/robotgo v0.110.8 - github.com/robotn/gohook v0.42.2 github.com/wailsapp/wails/v2 v2.11.0 - golang.design/x/clipboard v0.7.1 + golang.design/x/mainthread v0.3.0 golang.org/x/sys v0.33.0 ) @@ -66,9 +65,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.33.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect golang.org/x/image v0.28.0 // indirect - golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect golang.org/x/net v0.35.0 // indirect golang.org/x/text v0.26.0 // indirect ) diff --git a/go.sum b/go.sum index e235284..a665fb1 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,6 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:Om github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/robotn/gohook v0.42.2 h1:AI9OVh5o59c76jp9Xcc4NpIvze2YeKX1Rn8JvflAUXY= -github.com/robotn/gohook v0.42.2/go.mod h1:PYgH0f1EaxhCvNSqIVTfo+SIUh1MrM2Uhe2w7SvFJDE= github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= github.com/robotn/xgb v0.10.0 h1:O3kFbIwtwZ3pgLbp1h5slCQ4OpY8BdwugJLrUe6GPIM= github.com/robotn/xgb v0.10.0/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= @@ -139,24 +137,21 @@ github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSB github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c= -golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg= +golang.design/x/mainthread v0.3.0 h1:UwFus0lcPodNpMOGoQMe87jSFwbSsEY//CA7yVmu4j8= +golang.design/x/mainthread v0.3.0/go.mod h1:vYX7cF2b3pTJMGM/hc13NmN6kblKnf4/IyvHeu259L0= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 h1:Wdx0vgH5Wgsw+lF//LJKmWOJBLWX6nprsMqnf99rYDE= -golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8= golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= -golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8= -golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/hotkey/hotkey.go b/internal/hotkey/hotkey.go index ea8e64a..ff536fd 100644 --- a/internal/hotkey/hotkey.go +++ b/internal/hotkey/hotkey.go @@ -1,13 +1,11 @@ // Package hotkey provides global keyboard shortcut detection and handling. -// It uses the gohook library to capture system-wide key events. package hotkey import ( "sync" - + "time" "wis-free-v3/internal/logger" - - hook "github.com/robotn/gohook" + "wis-free-v3/internal/xhotkey" ) // Listener handles global hotkey events and triggers callbacks when the @@ -16,145 +14,128 @@ type Listener struct { startCallback func() stopCallback func() isListening bool - stopChan chan struct{} - triggerKeys []uint16 - modifiers [][]uint16 + shortcut string + hk *hotkey.Hotkey mu sync.RWMutex } // NewListener creates a new hotkey listener with the specified shortcut and callbacks. // The shortcut should be in format like "ctrl+k" or "alt+shift+space". -// onStart is called when the shortcut is pressed, onStop when it's released. func NewListener(shortcut string, onStart, onStop func()) *Listener { - trigger, mods := ParseShortcut(shortcut) - - logger.Info("Hotkey listener created: shortcut=%s, trigger=%d, modifiers=%v", - shortcut, trigger, mods) + logger.Info("Hotkey listener created: shortcut=%s", shortcut) return &Listener{ startCallback: onStart, stopCallback: onStop, - stopChan: make(chan struct{}), - triggerKeys: trigger, - modifiers: mods, + shortcut: shortcut, } } // UpdateShortcut changes the shortcut without stopping the listener. // This allows hot-swapping the shortcut while the application is running. func (l *Listener) UpdateShortcut(shortcut string) { - trigger, mods := ParseShortcut(shortcut) - l.mu.Lock() - l.triggerKeys = trigger - l.modifiers = mods + l.shortcut = shortcut + wasListening := l.isListening + + if l.hk != nil { + l.hk.Unregister() + l.hk = nil + l.isListening = false + } l.mu.Unlock() - logger.Info("Hotkey updated: shortcut=%s, trigger=%d", shortcut, trigger) + logger.Info("Hotkey updated: shortcut=%s", shortcut) + + if wasListening { + l.Start() + } } // Start begins listening for the configured shortcut in a background goroutine. -// It's safe to call Start multiple times; subsequent calls are ignored. func (l *Listener) Start() { - if l.isListening { + l.mu.Lock() + defer l.mu.Unlock() + + if l.hk != nil { return } - l.isListening = true - go l.eventLoop() + key, mods, ok := ParseShortcut(l.shortcut) + if !ok { + logger.Error("Failed to parse shortcut: %s", l.shortcut) + return + } + + l.hk = hotkey.New(mods, key) + if err := l.hk.Register(); err != nil { + logger.Error("Failed to register hotkey %s: %v", l.shortcut, err) + l.hk = nil + return + } + + l.isListening = true + logger.Info("Hotkey listener started, waiting for %s", l.shortcut) + + go l.eventLoop(l.hk) } // Stop terminates the hotkey listener. -// It's safe to call Stop multiple times. func (l *Listener) Stop() { - if !l.isListening { + l.mu.Lock() + defer l.mu.Unlock() + + if l.hk == nil { return } + + err := l.hk.Unregister() + if err != nil { + logger.Error("Failed to unregister hotkey: %v", err) + } + + l.hk = nil l.isListening = false - close(l.stopChan) logger.Info("Hotkey listener stopped") } // eventLoop runs the main keyboard event processing loop. -func (l *Listener) eventLoop() { - logger.Info("Hotkey listener started") - - evChan := hook.Start() - defer hook.End() - +func (l *Listener) eventLoop(hk *hotkey.Hotkey) { var isRecording bool - pressedKeys := make(map[uint16]bool) for { select { - case <-l.stopChan: - return + case _, ok := <-hk.Keydown(): + if !ok { + return // Hotkey was unregistered + } + if !isRecording { + logger.Info("Shortcut activated: starting recording") + go l.startCallback() + isRecording = true + } - case ev := <-evChan: - l.handleKeyEvent(ev, pressedKeys, &isRecording) - } - } -} - -// handleKeyEvent processes a single keyboard event. -func (l *Listener) handleKeyEvent(ev hook.Event, pressedKeys map[uint16]bool, isRecording *bool) { - // Update pressed keys state - switch ev.Kind { - case hook.KeyDown, hook.KeyHold: - pressedKeys[ev.Rawcode] = true - case hook.KeyUp: - delete(pressedKeys, ev.Rawcode) - default: - return - } - - // Read current configuration - l.mu.RLock() - triggerVariants := l.triggerKeys - modGroups := l.modifiers - l.mu.RUnlock() - - if len(triggerVariants) == 0 { - return - } - - // 1. Check trigger key first (fast path) - triggerPressed := false - for _, t := range triggerVariants { - if pressedKeys[t] { - triggerPressed = true - break - } - } - - // 2. State transition handling - active := false - if triggerPressed { - active = true - for _, group := range modGroups { - groupPressed := false - for _, m := range group { - if pressedKeys[m] { - groupPressed = true - break + case _, ok := <-hk.Keyup(): + if !ok { + return // Hotkey was unregistered + } + if isRecording { + // X11 AutoRepeat Debounce logic + // Wait a tiny fraction of a second. If we get a Keydown during this window, + // it's an auto-repeat from holding the key, so we ignore both the Keyup and Keydown. + select { + case _, downOk := <-hk.Keydown(): + if !downOk { + return + } + // AutoRepeat detected, skip this release! + case <-time.After(50 * time.Millisecond): + // Key was genuinely physically released + logger.Info("Shortcut released: stopping recording") + go l.stopCallback() + isRecording = false } } - if !groupPressed { - active = false - break - } } } - - if active && !*isRecording { - logger.Info("Shortcut activated: starting recording") - go l.startCallback() - *isRecording = true - } else if !active && *isRecording { - logger.Info("Shortcut released: stopping recording") - go l.stopCallback() - *isRecording = false - } } - - diff --git a/internal/hotkey/keycodes.go b/internal/hotkey/keycodes.go index 9c3abb6..9c9a718 100644 --- a/internal/hotkey/keycodes.go +++ b/internal/hotkey/keycodes.go @@ -1,112 +1,98 @@ -// Package hotkey provides global keyboard shortcut detection and handling. package hotkey -import "strings" +import ( + "strings" -// Windows Virtual Key Codes -// See: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes -const ( - VK_LCTRL = 162 // Left Control - VK_RCTRL = 163 // Right Control - VK_LSHIFT = 160 // Left Shift - VK_RSHIFT = 161 // Right Shift - VK_LALT = 164 // Left Alt (Menu) - VK_RALT = 165 // Right Alt (Menu) - VK_LWIN = 91 // Left Windows key - VK_RWIN = 92 // Right Windows key + "wis-free-v3/internal/xhotkey" ) -// keyMap maps human-readable key names to Windows Virtual Key Codes. -// Each key may have multiple valid codes (e.g., left and right variants). -var keyMap = map[string][]uint16{ - // Modifier keys - "ctrl": {VK_LCTRL, VK_RCTRL, 17}, - "control": {VK_LCTRL, VK_RCTRL, 17}, - "shift": {VK_LSHIFT, VK_RSHIFT, 16}, - "alt": {VK_LALT, VK_RALT, 18}, - "win": {VK_LWIN, VK_RWIN, 91, 92}, - "windows": {VK_LWIN, VK_RWIN, 91, 92}, - "meta": {VK_LWIN, VK_RWIN, 91, 92}, - "super": {VK_LWIN, VK_RWIN, 91, 92}, - - // Special keys - "backspace": {8}, - "tab": {9}, - "enter": {13}, - "escape": {27}, - "esc": {27}, - "space": {32}, - "left": {37}, - "up": {38}, - "right": {39}, - "down": {40}, - "delete": {46}, - "del": {46}, - - // Number keys (top row) - "0": {48}, "1": {49}, "2": {50}, "3": {51}, "4": {52}, - "5": {53}, "6": {54}, "7": {55}, "8": {56}, "9": {57}, - - // Letter keys - "a": {65}, "b": {66}, "c": {67}, "d": {68}, "e": {69}, - "f": {70}, "g": {71}, "h": {72}, "i": {73}, "j": {74}, - "k": {75}, "l": {76}, "m": {77}, "n": {78}, "o": {79}, - "p": {80}, "q": {81}, "r": {82}, "s": {83}, "t": {84}, - "u": {85}, "v": {86}, "w": {87}, "x": {88}, "y": {89}, - "z": {90}, - - // Function keys - "f1": {112}, "f2": {113}, "f3": {114}, "f4": {115}, - "f5": {116}, "f6": {117}, "f7": {118}, "f8": {119}, - "f9": {120}, "f10": {121}, "f11": {122}, "f12": {123}, +// KeyMap maps human-readable key names to golang.design/x/hotkey Key instances. +var KeyMap = map[string]hotkey.Key{ + "space": hotkey.KeySpace, + "0": hotkey.Key0, + "1": hotkey.Key1, + "2": hotkey.Key2, + "3": hotkey.Key3, + "4": hotkey.Key4, + "5": hotkey.Key5, + "6": hotkey.Key6, + "7": hotkey.Key7, + "8": hotkey.Key8, + "9": hotkey.Key9, + "a": hotkey.KeyA, + "b": hotkey.KeyB, + "c": hotkey.KeyC, + "d": hotkey.KeyD, + "e": hotkey.KeyE, + "f": hotkey.KeyF, + "g": hotkey.KeyG, + "h": hotkey.KeyH, + "i": hotkey.KeyI, + "j": hotkey.KeyJ, + "k": hotkey.KeyK, + "l": hotkey.KeyL, + "m": hotkey.KeyM, + "n": hotkey.KeyN, + "o": hotkey.KeyO, + "p": hotkey.KeyP, + "q": hotkey.KeyQ, + "r": hotkey.KeyR, + "s": hotkey.KeyS, + "t": hotkey.KeyT, + "u": hotkey.KeyU, + "v": hotkey.KeyV, + "w": hotkey.KeyW, + "x": hotkey.KeyX, + "y": hotkey.KeyY, + "z": hotkey.KeyZ, + "return": hotkey.KeyReturn, + "enter": hotkey.KeyReturn, + "escape": hotkey.KeyEscape, + "esc": hotkey.KeyEscape, + "delete": hotkey.KeyDelete, + "del": hotkey.KeyDelete, + "tab": hotkey.KeyTab, + "left": hotkey.KeyLeft, + "right": hotkey.KeyRight, + "up": hotkey.KeyUp, + "down": hotkey.KeyDown, + "f1": hotkey.KeyF1, + "f2": hotkey.KeyF2, + "f3": hotkey.KeyF3, + "f4": hotkey.KeyF4, + "f5": hotkey.KeyF5, + "f6": hotkey.KeyF6, + "f7": hotkey.KeyF7, + "f8": hotkey.KeyF8, + "f9": hotkey.KeyF9, + "f10": hotkey.KeyF10, + "f11": hotkey.KeyF11, + "f12": hotkey.KeyF12, } -// modifierKeys identifies which key names are modifiers. -var modifierKeys = map[string]bool{ - "ctrl": true, "control": true, - "shift": true, - "alt": true, - "win": true, "windows": true, "meta": true, "super": true, -} - -// ParseShortcut parses a shortcut string into a trigger key and modifier keys. -// The shortcut format is "modifier+modifier+key" (e.g., "ctrl+shift+k"). -// -// Returns: -// - trigger: the primary key code that activates the shortcut -// - modifiers: slice of modifier key code variants that must be held -func ParseShortcut(shortcut string) (trigger []uint16, modifiers [][]uint16) { +// ParseShortcut parses a shortcut string and returns the hotkey.Key and a list of modifiers. +// Returns false if parsing fails. +func ParseShortcut(shortcut string) (hotkey.Key, []hotkey.Modifier, bool) { parts := strings.Split(strings.ToLower(strings.TrimSpace(shortcut)), "+") if len(parts) == 0 { - return nil, nil + return 0, nil, false } - // Last part is always the trigger key + // The last part is the trigger key triggerName := strings.TrimSpace(parts[len(parts)-1]) - if codes, ok := keyMap[triggerName]; ok && len(codes) > 0 { - trigger = codes + k, exists := KeyMap[triggerName] + if !exists { + return 0, nil, false } - // Preceding parts are modifiers + // The preceding parts are modifiers + var mods []hotkey.Modifier for i := 0; i < len(parts)-1; i++ { modName := strings.TrimSpace(parts[i]) - if codes, ok := keyMap[modName]; ok { - modifiers = append(modifiers, codes) + if m, ok := ModMap[modName]; ok { + mods = append(mods, m) } } - return trigger, modifiers + return k, mods, true } - -// IsModifier returns true if the given key name is a modifier key. -func IsModifier(keyName string) bool { - return modifierKeys[strings.ToLower(keyName)] -} - -// GetKeyCode returns the virtual key code(s) for a given key name. -// Returns nil if the key name is not recognized. -func GetKeyCode(keyName string) []uint16 { - return keyMap[strings.ToLower(keyName)] -} - - diff --git a/internal/hotkey/modifiers_darwin.go b/internal/hotkey/modifiers_darwin.go new file mode 100644 index 0000000..326bf31 --- /dev/null +++ b/internal/hotkey/modifiers_darwin.go @@ -0,0 +1,17 @@ +//go:build darwin + +package hotkey + +import "wis-free-v3/internal/xhotkey" + +var ModMap = map[string]hotkey.Modifier{ + "ctrl": hotkey.ModCtrl, + "control": hotkey.ModCtrl, + "shift": hotkey.ModShift, + "alt": hotkey.ModOption, + "win": hotkey.ModCmd, + "windows": hotkey.ModCmd, + "meta": hotkey.ModCmd, + "super": hotkey.ModCmd, + "cmd": hotkey.ModCmd, +} diff --git a/internal/hotkey/modifiers_linux.go b/internal/hotkey/modifiers_linux.go new file mode 100644 index 0000000..2ead039 --- /dev/null +++ b/internal/hotkey/modifiers_linux.go @@ -0,0 +1,16 @@ +//go:build linux + +package hotkey + +import "wis-free-v3/internal/xhotkey" + +var ModMap = map[string]hotkey.Modifier{ + "ctrl": hotkey.ModCtrl, + "control": hotkey.ModCtrl, + "shift": hotkey.ModShift, + "alt": hotkey.Mod1, + "win": hotkey.Mod4, + "windows": hotkey.Mod4, + "meta": hotkey.Mod4, + "super": hotkey.Mod4, +} diff --git a/internal/hotkey/modifiers_windows.go b/internal/hotkey/modifiers_windows.go new file mode 100644 index 0000000..7a0918e --- /dev/null +++ b/internal/hotkey/modifiers_windows.go @@ -0,0 +1,16 @@ +//go:build windows + +package hotkey + +import "wis-free-v3/internal/xhotkey" + +var ModMap = map[string]hotkey.Modifier{ + "ctrl": hotkey.ModCtrl, + "control": hotkey.ModCtrl, + "shift": hotkey.ModShift, + "alt": hotkey.ModAlt, + "win": hotkey.ModWin, + "windows": hotkey.ModWin, + "meta": hotkey.ModWin, + "super": hotkey.ModWin, +} diff --git a/internal/ui/tray/icon_recording.png b/internal/ui/tray/icon_recording.png new file mode 100644 index 0000000..f3253a4 Binary files /dev/null and b/internal/ui/tray/icon_recording.png differ diff --git a/internal/ui/tray/icon_transcribing.png b/internal/ui/tray/icon_transcribing.png new file mode 100644 index 0000000..aae7637 Binary files /dev/null and b/internal/ui/tray/icon_transcribing.png differ diff --git a/internal/ui/tray/tray.go b/internal/ui/tray/tray.go index 5280f55..ef6ae10 100644 --- a/internal/ui/tray/tray.go +++ b/internal/ui/tray/tray.go @@ -5,6 +5,7 @@ package tray import ( _ "embed" "os" + "strings" "wis-free-v3/internal/config" "wis-free-v3/internal/logger" @@ -16,6 +17,12 @@ import ( //go:embed icon.ico var iconData []byte +//go:embed icon_recording.png +var iconRecordingData []byte + +//go:embed icon_transcribing.png +var iconTranscribingData []byte + // App defines the interface required by the tray package to interact with the main application. type App interface { Quit() @@ -120,6 +127,14 @@ func UpdateStatus(status string) { if statusMenuItem != nil { statusMenuItem.SetTitle("Status: " + status) systray.SetTooltip("wis-free-v3 - " + status) + + if strings.Contains(status, "Recording") { + systray.SetIcon(iconRecordingData) + } else if strings.Contains(status, "Transcribing") { + systray.SetIcon(iconTranscribingData) + } else { + systray.SetIcon(iconData) + } } } diff --git a/internal/xhotkey/.github/FUNDING.yml b/internal/xhotkey/.github/FUNDING.yml new file mode 100644 index 0000000..30bf190 --- /dev/null +++ b/internal/xhotkey/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [changkun] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] \ No newline at end of file diff --git a/internal/xhotkey/.github/workflows/hotkey.yml b/internal/xhotkey/.github/workflows/hotkey.yml new file mode 100644 index 0000000..3595a2f --- /dev/null +++ b/internal/xhotkey/.github/workflows/hotkey.yml @@ -0,0 +1,65 @@ +# Copyright 2021 The golang.design Initiative Authors. +# All rights reserved. Use of this source code is governed +# by a MIT license that can be found in the LICENSE file. +# +# Written by Changkun Ou + +name: hotkey + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + platform_test: + env: + DISPLAY: ':0.0' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + go: ['1.17.x', '1.18.x', '1.19.x'] + steps: + - name: Install and run dependencies (xvfb libx11-dev) + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt update + sudo apt install -y xvfb libx11-dev x11-utils libegl1-mesa-dev libgles2-mesa-dev + Xvfb :0 -screen 0 1024x768x24 > /dev/null 2>&1 & + # Wait for Xvfb + MAX_ATTEMPTS=120 # About 60 seconds + COUNT=0 + echo -n "Waiting for Xvfb to be ready..." + while ! xdpyinfo -display "${DISPLAY}" >/dev/null 2>&1; do + echo -n "." + sleep 0.50s + COUNT=$(( COUNT + 1 )) + if [ "${COUNT}" -ge "${MAX_ATTEMPTS}" ]; then + echo " Gave up waiting for X server on ${DISPLAY}" + exit 1 + fi + done + echo "Done - Xvfb is ready!" + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + stable: 'false' + go-version: ${{ matrix.go }} + + - name: Run Tests with CGO_ENABLED=1 + if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}} + run: | + CGO_ENABLED=1 go test -v -covermode=atomic . + + - name: Run Tests with CGO_ENABLED=0 + if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}} + run: | + CGO_ENABLED=0 go test -v -covermode=atomic . + + - name: Run Tests on Windows + if: ${{ runner.os == 'Windows'}} + run: | + go test -v -covermode=atomic . \ No newline at end of file diff --git a/internal/xhotkey/.gitignore b/internal/xhotkey/.gitignore new file mode 100644 index 0000000..66fd13c --- /dev/null +++ b/internal/xhotkey/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/internal/xhotkey/LICENSE b/internal/xhotkey/LICENSE new file mode 100644 index 0000000..c84d182 --- /dev/null +++ b/internal/xhotkey/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Changkun Ou + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/internal/xhotkey/README.md b/internal/xhotkey/README.md new file mode 100644 index 0000000..40abcb9 --- /dev/null +++ b/internal/xhotkey/README.md @@ -0,0 +1,90 @@ +# hotkey [![PkgGoDev](https://pkg.go.dev/badge/golang.design/x/hotkey)](https://pkg.go.dev/golang.design/x/hotkey) ![](https://changkun.de/urlstat?mode=github&repo=golang-design/hotkey) ![hotkey](https://github.com/golang-design/hotkey/workflows/hotkey/badge.svg?branch=main) + +cross platform hotkey package in Go + +```go +import "golang.design/x/hotkey" +``` + +## Features + +- Cross platform supports: macOS, Linux (X11), and Windows +- Global hotkey registration without focus on a window + +## API Usage + +Package hotkey provides the basic facility to register a system-level +global hotkey shortcut so that an application can be notified if a user +triggers the desired hotkey. A hotkey must be a combination of modifiers +and a single key. + +```go +package main + +import ( + "log" + + "golang.design/x/hotkey" + "golang.design/x/hotkey/mainthread" +) + +func main() { mainthread.Init(fn) } // Not necessary when use in Fyne, Ebiten or Gio. +func fn() { + hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS) + err := hk.Register() + if err != nil { + log.Fatalf("hotkey: failed to register hotkey: %v", err) + return + } + + log.Printf("hotkey: %v is registered\n", hk) + <-hk.Keydown() + log.Printf("hotkey: %v is down\n", hk) + <-hk.Keyup() + log.Printf("hotkey: %v is up\n", hk) + hk.Unregister() + log.Printf("hotkey: %v is unregistered\n", hk) +} +``` + +Note platform specific details: + +- On macOS, due to the OS restriction (other platforms does not have this + restriction), hotkey events must be handled on the "main thread". + Therefore, in order to use this package properly, one must start an OS + main event loop on the main thread, For self-contained applications, + using [golang.design/x/hotkey/mainthread](https://pkg.go.dev/golang.design/x/hotkey/mainthread) + is possible. It is uncessary or applications based on other GUI frameworks, + such as fyne, ebiten, or Gio. See the "[./examples](./examples)" folder + for more examples. +- On Linux (X11), when AutoRepeat is enabled in the X server, the Keyup + is triggered automatically and continuously as Keydown continues. +- On Linux (X11), some keys may be mapped to multiple Mod keys. To + correctly register the key combination, one must use the correct + underlying keycode combination. For example, a regular Ctrl+Alt+S + might be registered as: Ctrl+Mod2+Mod4+S. +- If this package did not include a desired key, one can always provide + the keycode to the API. For example, if a key code is 0x15, then the + corresponding key is `hotkey.Key(0x15)`. + +## Examples + +| Description | Folder | +|:------------|:------:| +| A minimum example | [minimum](./examples/minimum/main.go) | +| Register multiple hotkeys | [multiple](./examples/multiple/main.go) | +| A example to use in GLFW | [glfw](./examples/glfw/main.go) | +| A example to use in Fyne | [fyne](./examples/fyne/main.go) | +| A example to use in Ebiten | [ebiten](./examples/ebiten/main.go) | +| A example to use in Gio | [gio](./examples/gio/main.go) | + +## Who is using this package? + +The main purpose of building this package is to support the +[midgard](https://changkun.de/s/midgard) project. + +To know more projects, check our [wiki](https://github.com/golang-design/hotkey/wiki) page. + +## License + +MIT | © 2021 The golang.design Initiative Authors, written by [Changkun Ou](https://changkun.de). \ No newline at end of file diff --git a/internal/xhotkey/hotkey.go b/internal/xhotkey/hotkey.go new file mode 100644 index 0000000..8a0b8a8 --- /dev/null +++ b/internal/xhotkey/hotkey.go @@ -0,0 +1,181 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +// Package hotkey provides the basic facility to register a system-level +// global hotkey shortcut so that an application can be notified if a user +// triggers the desired hotkey. A hotkey must be a combination of modifiers +// and a single key. +// +// Note platform specific details: +// +// - On macOS, due to the OS restriction (other platforms does not have +// this restriction), hotkey events must be handled on the "main thread". +// Therefore, in order to use this package properly, one must start an +// OS main event loop on the main thread, For self-contained applications, +// using [mainthread] package. +// is possible. It is uncessary or applications based on other GUI frameworks, +// such as fyne, ebiten, or Gio. See the "[examples]" for more examples. +// +// - On Linux (X11), when AutoRepeat is enabled in the X server, the +// Keyup is triggered automatically and continuously as Keydown continues. +// +// - On Linux (X11), some keys may be mapped to multiple Mod keys. To +// correctly register the key combination, one must use the correct +// underlying keycode combination. For example, a regular Ctrl+Alt+S +// might be registered as: Ctrl+Mod2+Mod4+S. +// +// - If this package did not include a desired key, one can always provide +// the keycode to the API. For example, if a key code is 0x15, then the +// corresponding key is `hotkey.Key(0x15)`. +// +// THe following is a minimum example: +// +// package main +// +// import ( +// "log" +// +// "wis-free-v3/internal/xhotkey" +// "wis-free-v3/internal/xhotkey/mainthread" +// ) +// +// func main() { mainthread.Init(fn) } // Not necessary when use in Fyne, Ebiten or Gio. +// func fn() { +// hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS) +// err := hk.Register() +// if err != nil { +// log.Fatalf("hotkey: failed to register hotkey: %v", err) +// } +// +// log.Printf("hotkey: %v is registered\n", hk) +// <-hk.Keydown() +// log.Printf("hotkey: %v is down\n", hk) +// <-hk.Keyup() +// log.Printf("hotkey: %v is up\n", hk) +// hk.Unregister() +// log.Printf("hotkey: %v is unregistered\n", hk) +// } +// +// [mainthread]: https://pkg.go.dev/golang.design/x/hotkey/mainthread +// [examples]: https://github.com/golang-design/hotkey/tree/main/examples +package hotkey + +import ( + "fmt" + "runtime" +) + +// Event represents a hotkey event +type Event struct{} + +// Hotkey is a combination of modifiers and key to trigger an event +type Hotkey struct { + platformHotkey + + mods []Modifier + key Key + + keydownIn chan<- Event + keydownOut <-chan Event + keyupIn chan<- Event + keyupOut <-chan Event +} + +// New creates a new hotkey for the given modifiers and keycode. +func New(mods []Modifier, key Key) *Hotkey { + keydownIn, keydownOut := newEventChan() + keyupIn, keyupOut := newEventChan() + hk := &Hotkey{ + mods: mods, + key: key, + keydownIn: keydownIn, + keydownOut: keydownOut, + keyupIn: keyupIn, + keyupOut: keyupOut, + } + + // Make sure the hotkey is unregistered when the created + // hotkey is garbage collected. + runtime.SetFinalizer(hk, func(x interface{}) { + hk := x.(*Hotkey) + hk.unregister() + close(hk.keydownIn) + close(hk.keyupIn) + }) + return hk +} + +// Register registers a combination of hotkeys. If the hotkey has +// registered. This function will invalidates the old registration +// and overwrites its callback. +func (hk *Hotkey) Register() error { return hk.register() } + +// Keydown returns a channel that receives a signal when the hotkey is triggered. +func (hk *Hotkey) Keydown() <-chan Event { return hk.keydownOut } + +// Keyup returns a channel that receives a signal when the hotkey is released. +func (hk *Hotkey) Keyup() <-chan Event { return hk.keyupOut } + +// Unregister unregisters the hotkey. +func (hk *Hotkey) Unregister() error { + err := hk.unregister() + if err != nil { + return err + } + + // Reset a new event channel. + close(hk.keydownIn) + close(hk.keyupIn) + hk.keydownIn, hk.keydownOut = newEventChan() + hk.keyupIn, hk.keyupOut = newEventChan() + return nil +} + +// String returns a string representation of the hotkey. +func (hk *Hotkey) String() string { + s := fmt.Sprintf("%v", hk.key) + for _, mod := range hk.mods { + s += fmt.Sprintf("+%v", mod) + } + return s +} + +// newEventChan returns a sender and a receiver of a buffered channel +// with infinite capacity. +func newEventChan() (chan<- Event, <-chan Event) { + in, out := make(chan Event), make(chan Event) + + go func() { + var q []Event + + for { + e, ok := <-in + if !ok { + close(out) + return + } + q = append(q, e) + for len(q) > 0 { + select { + case out <- q[0]: + q[0] = Event{} + q = q[1:] + case e, ok := <-in: + if ok { + q = append(q, e) + break + } + for _, e := range q { + out <- e + } + close(out) + return + } + } + } + }() + return in, out +} diff --git a/internal/xhotkey/hotkey_darwin.go b/internal/xhotkey/hotkey_darwin.go new file mode 100644 index 0000000..25a90b6 --- /dev/null +++ b/internal/xhotkey/hotkey_darwin.go @@ -0,0 +1,176 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build darwin + +package hotkey + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa -framework Carbon +#include +#import +#import + +extern void keydownCallback(uintptr_t handle); +extern void keyupCallback(uintptr_t handle); +int registerHotKey(int mod, int key, uintptr_t handle, EventHotKeyRef* ref); +int unregisterHotKey(EventHotKeyRef ref); +*/ +import "C" +import ( + "errors" + "runtime/cgo" + "sync" +) + +// Hotkey is a combination of modifiers and key to trigger an event +type platformHotkey struct { + mu sync.Mutex + registered bool + hkref C.EventHotKeyRef +} + +func (hk *Hotkey) register() error { + hk.mu.Lock() + defer hk.mu.Unlock() + if hk.registered { + return errors.New("hotkey already registered") + } + + // Note: we use handle number as hotkey id in the C side. + // A cgo handle could ran out of space, but since in hotkey purpose + // we won't have that much number of hotkeys. So this should be fine. + + h := cgo.NewHandle(hk) + var mod Modifier + for _, m := range hk.mods { + mod += m + } + + ret := C.registerHotKey(C.int(mod), C.int(hk.key), C.uintptr_t(h), &hk.hkref) + if ret == C.int(-1) { + return errors.New("failed to register the hotkey") + } + + hk.registered = true + return nil +} + +func (hk *Hotkey) unregister() error { + hk.mu.Lock() + defer hk.mu.Unlock() + if !hk.registered { + return errors.New("hotkey is not registered") + } + + ret := C.unregisterHotKey(hk.hkref) + if ret == C.int(-1) { + return errors.New("failed to unregister the current hotkey") + } + hk.registered = false + return nil +} + +//export keydownCallback +func keydownCallback(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keydownIn <- Event{} +} + +//export keyupCallback +func keyupCallback(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keyupIn <- Event{} +} + +// Modifier represents a modifier. +// See: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h +type Modifier uint32 + +// All kinds of Modifiers +const ( + ModCtrl Modifier = 0x1000 + ModShift Modifier = 0x200 + ModOption Modifier = 0x800 + ModCmd Modifier = 0x100 +) + +// Key represents a key. +// See: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h +type Key uint8 + +// All kinds of keys +const ( + KeySpace Key = 49 + Key1 Key = 18 + Key2 Key = 19 + Key3 Key = 20 + Key4 Key = 21 + Key5 Key = 23 + Key6 Key = 22 + Key7 Key = 26 + Key8 Key = 28 + Key9 Key = 25 + Key0 Key = 29 + KeyA Key = 0 + KeyB Key = 11 + KeyC Key = 8 + KeyD Key = 2 + KeyE Key = 14 + KeyF Key = 3 + KeyG Key = 5 + KeyH Key = 4 + KeyI Key = 34 + KeyJ Key = 38 + KeyK Key = 40 + KeyL Key = 37 + KeyM Key = 46 + KeyN Key = 45 + KeyO Key = 31 + KeyP Key = 35 + KeyQ Key = 12 + KeyR Key = 15 + KeyS Key = 1 + KeyT Key = 17 + KeyU Key = 32 + KeyV Key = 9 + KeyW Key = 13 + KeyX Key = 7 + KeyY Key = 16 + KeyZ Key = 6 + + KeyReturn Key = 0x24 + KeyEscape Key = 0x35 + KeyDelete Key = 0x33 + KeyTab Key = 0x30 + + KeyLeft Key = 0x7B + KeyRight Key = 0x7C + KeyUp Key = 0x7E + KeyDown Key = 0x7D + + KeyF1 Key = 0x7A + KeyF2 Key = 0x78 + KeyF3 Key = 0x63 + KeyF4 Key = 0x76 + KeyF5 Key = 0x60 + KeyF6 Key = 0x61 + KeyF7 Key = 0x62 + KeyF8 Key = 0x64 + KeyF9 Key = 0x65 + KeyF10 Key = 0x6D + KeyF11 Key = 0x67 + KeyF12 Key = 0x6F + KeyF13 Key = 0x69 + KeyF14 Key = 0x6B + KeyF15 Key = 0x71 + KeyF16 Key = 0x6A + KeyF17 Key = 0x40 + KeyF18 Key = 0x4F + KeyF19 Key = 0x50 + KeyF20 Key = 0x5A +) diff --git a/internal/xhotkey/hotkey_darwin.m b/internal/xhotkey/hotkey_darwin.m new file mode 100644 index 0000000..3463bea --- /dev/null +++ b/internal/xhotkey/hotkey_darwin.m @@ -0,0 +1,65 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build darwin + +#include +#import +#import +extern void keydownCallback(uintptr_t handle); +extern void keyupCallback(uintptr_t handle); + +static OSStatus +keydownHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) { + EventHotKeyID k; + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(k), NULL, &k); + keydownCallback((uintptr_t)k.id); // use id as handle + return noErr; +} + +static OSStatus +keyupHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) { + EventHotKeyID k; + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(k), NULL, &k); + keyupCallback((uintptr_t)k.id); // use id as handle + return noErr; +} + +// registerHotkeyWithCallback registers a global system hotkey for callbacks. +int registerHotKey(int mod, int key, uintptr_t handle, EventHotKeyRef* ref) { + __block OSStatus s; + dispatch_sync(dispatch_get_main_queue(), ^{ + EventTypeSpec keydownEvent; + keydownEvent.eventClass = kEventClassKeyboard; + keydownEvent.eventKind = kEventHotKeyPressed; + EventTypeSpec keyupEvent; + keyupEvent.eventClass = kEventClassKeyboard; + keyupEvent.eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler( + &keydownHandler, 1, &keydownEvent, NULL, NULL + ); + InstallApplicationEventHandler( + &keyupHandler, 1, &keyupEvent, NULL, NULL + ); + + EventHotKeyID hkid = {.id = handle}; + s = RegisterEventHotKey( + key, mod, hkid, GetApplicationEventTarget(), 0, ref + ); + }); + if (s != noErr) { + return -1; + } + return 0; +} + +int unregisterHotKey(EventHotKeyRef ref) { + OSStatus s = UnregisterEventHotKey(ref); + if (s != noErr) { + return -1; + } + return 0; +} diff --git a/internal/xhotkey/hotkey_darwin_test.go b/internal/xhotkey/hotkey_darwin_test.go new file mode 100644 index 0000000..2569b6d --- /dev/null +++ b/internal/xhotkey/hotkey_darwin_test.go @@ -0,0 +1,68 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build darwin && cgo + +package hotkey_test + +import ( + "context" + "fmt" + "testing" + "time" + + "wis-free-v3/internal/xhotkey" +) + +// TestHotkey should always run success. +// This is a test to run and for manually testing the registration of multiple +// hotkeys. Registered hotkeys: +// Ctrl+Shift+S +// Ctrl+Option+S +func TestHotkey(t *testing.T) { + tt := time.Second * 5 + done := make(chan struct{}, 2) + ctx, cancel := context.WithTimeout(context.Background(), tt) + go func() { + hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS) + if err := hk.Register(); err != nil { + t.Errorf("failed to register hotkey: %v", err) + return + } + for { + select { + case <-ctx.Done(): + cancel() + done <- struct{}{} + return + case <-hk.Keydown(): + fmt.Println("triggered ctrl+shift+s") + } + } + }() + + go func() { + hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModOption}, hotkey.KeyS) + if err := hk.Register(); err != nil { + t.Errorf("failed to register hotkey: %v", err) + return + } + + for { + select { + case <-ctx.Done(): + cancel() + done <- struct{}{} + return + case <-hk.Keydown(): + fmt.Println("triggered ctrl+option+s") + } + } + }() + + <-done + <-done +} diff --git a/internal/xhotkey/hotkey_linux.c b/internal/xhotkey/hotkey_linux.c new file mode 100644 index 0000000..a2cc7c2 --- /dev/null +++ b/internal/xhotkey/hotkey_linux.c @@ -0,0 +1,76 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build linux + +#include +#include +#include +#include +#include +#include + +extern void hotkeyDown(uintptr_t hkhandle); +extern void hotkeyUp(uintptr_t hkhandle); +extern int checkCancel(uintptr_t hkhandle); + +int displayTest() { + Display* d = NULL; + for (int i = 0; i < 42; i++) { + d = XOpenDisplay(0); + if (d == NULL) continue; + break; + } + if (d == NULL) { + return -1; + } + return 0; +} + +// waitHotkey blocks until the hotkey is triggered. +int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) { + Display* d = NULL; + for (int i = 0; i < 42; i++) { + d = XOpenDisplay(0); + if (d == NULL) continue; + break; + } + if (d == NULL) { + return -1; + } + + // Optional: Ask X server to only send one release at the physical end of auto-repeat. + Bool supported; + XkbSetDetectableAutoRepeat(d, True, &supported); + + int keycode = XKeysymToKeycode(d, key); + XGrabKey(d, keycode, mod, DefaultRootWindow(d), False, GrabModeAsync, GrabModeAsync); + XSelectInput(d, DefaultRootWindow(d), KeyPressMask | KeyReleaseMask); + XEvent ev; + + while(1) { + if (checkCancel(hkhandle) == 1) { + break; + } + if (XPending(d) > 0) { + XNextEvent(d, &ev); + switch(ev.type) { + case KeyPress: + hotkeyDown(hkhandle); + continue; + case KeyRelease: + hotkeyUp(hkhandle); + continue; + } + } else { + usleep(10000); // Poll every 10ms for snappy responsiveness without high CPU + } + } + + XUngrabKey(d, keycode, mod, DefaultRootWindow(d)); + XCloseDisplay(d); + return 0; +} \ No newline at end of file diff --git a/internal/xhotkey/hotkey_linux.go b/internal/xhotkey/hotkey_linux.go new file mode 100644 index 0000000..c98d351 --- /dev/null +++ b/internal/xhotkey/hotkey_linux.go @@ -0,0 +1,218 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build linux + +package hotkey + +/* +#cgo LDFLAGS: -lX11 + +#include + +int displayTest(); +int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key); +*/ +import "C" +import ( + "context" + "errors" + "runtime" + "runtime/cgo" + "sync" +) + +const errmsg = `Failed to initialize the X11 display, and the clipboard package +will not work properly. Install the following dependency may help: + + apt install -y libx11-dev +If the clipboard package is in an environment without a frame buffer, +such as a cloud server, it may also be necessary to install xvfb: + apt install -y xvfb +and initialize a virtual frame buffer: + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + export DISPLAY=:99.0 +Then this package should be ready to use. +` + +func init() { + if C.displayTest() != 0 { + panic(errmsg) + } +} + +type platformHotkey struct { + mu sync.Mutex + registered bool + ctx context.Context + cancel context.CancelFunc + canceled chan struct{} +} + +// Nothing needs to do for register +func (hk *Hotkey) register() error { + hk.mu.Lock() + if hk.registered { + hk.mu.Unlock() + return errors.New("hotkey already registered.") + } + hk.registered = true + hk.ctx, hk.cancel = context.WithCancel(context.Background()) + hk.canceled = make(chan struct{}) + hk.mu.Unlock() + + go hk.handle() + return nil +} + +// Nothing needs to do for unregister +func (hk *Hotkey) unregister() error { + hk.mu.Lock() + defer hk.mu.Unlock() + if !hk.registered { + return errors.New("hotkey is not registered.") + } + hk.cancel() + hk.registered = false + <-hk.canceled + return nil +} + +// handle registers an application global hotkey to the system, +// and returns a channel that will signal if the hotkey is triggered. +func (hk *Hotkey) handle() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + // KNOWN ISSUE: if a hotkey is grabbed by others, C side will crash the program + + var mod Modifier + for _, m := range hk.mods { + mod = mod | m + } + h := cgo.NewHandle(hk) + defer h.Delete() + + for { + // Just call it once! The C code handles its own loop until checkCancel returns 1. + _ = C.waitHotkey(C.uintptr_t(h), C.uint(mod), C.int(hk.key)) + close(hk.canceled) + return + } +} + +//export checkCancel +func checkCancel(h uintptr) C.int { + hk := cgo.Handle(h).Value().(*Hotkey) + select { + case <-hk.ctx.Done(): + return 1 + default: + return 0 + } +} + +//export hotkeyDown +func hotkeyDown(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keydownIn <- Event{} +} + +//export hotkeyUp +func hotkeyUp(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keyupIn <- Event{} +} + +// Modifier represents a modifier. +type Modifier uint32 + +// All kinds of Modifiers +// See /usr/include/X11/X.h +const ( + ModCtrl Modifier = (1 << 2) + ModShift Modifier = (1 << 0) + Mod1 Modifier = (1 << 3) + Mod2 Modifier = (1 << 4) + Mod3 Modifier = (1 << 5) + Mod4 Modifier = (1 << 6) + Mod5 Modifier = (1 << 7) +) + +// Key represents a key. +// See /usr/include/X11/keysymdef.h +type Key uint16 + +// All kinds of keys +const ( + KeySpace Key = 0x0020 + Key1 Key = 0x0030 + Key2 Key = 0x0031 + Key3 Key = 0x0032 + Key4 Key = 0x0033 + Key5 Key = 0x0034 + Key6 Key = 0x0035 + Key7 Key = 0x0036 + Key8 Key = 0x0037 + Key9 Key = 0x0038 + Key0 Key = 0x0039 + KeyA Key = 0x0061 + KeyB Key = 0x0062 + KeyC Key = 0x0063 + KeyD Key = 0x0064 + KeyE Key = 0x0065 + KeyF Key = 0x0066 + KeyG Key = 0x0067 + KeyH Key = 0x0068 + KeyI Key = 0x0069 + KeyJ Key = 0x006a + KeyK Key = 0x006b + KeyL Key = 0x006c + KeyM Key = 0x006d + KeyN Key = 0x006e + KeyO Key = 0x006f + KeyP Key = 0x0070 + KeyQ Key = 0x0071 + KeyR Key = 0x0072 + KeyS Key = 0x0073 + KeyT Key = 0x0074 + KeyU Key = 0x0075 + KeyV Key = 0x0076 + KeyW Key = 0x0077 + KeyX Key = 0x0078 + KeyY Key = 0x0079 + KeyZ Key = 0x007a + + KeyReturn Key = 0xff0d + KeyEscape Key = 0xff1b + KeyDelete Key = 0xffff + KeyTab Key = 0xff1b + + KeyLeft Key = 0xff51 + KeyRight Key = 0xff53 + KeyUp Key = 0xff52 + KeyDown Key = 0xff54 + + KeyF1 Key = 0xffbe + KeyF2 Key = 0xffbf + KeyF3 Key = 0xffc0 + KeyF4 Key = 0xffc1 + KeyF5 Key = 0xffc2 + KeyF6 Key = 0xffc3 + KeyF7 Key = 0xffc4 + KeyF8 Key = 0xffc5 + KeyF9 Key = 0xffc6 + KeyF10 Key = 0xffc7 + KeyF11 Key = 0xffc8 + KeyF12 Key = 0xffc9 + KeyF13 Key = 0xffca + KeyF14 Key = 0xffcb + KeyF15 Key = 0xffcc + KeyF16 Key = 0xffcd + KeyF17 Key = 0xffce + KeyF18 Key = 0xffcf + KeyF19 Key = 0xffd0 + KeyF20 Key = 0xffd1 +) diff --git a/internal/xhotkey/hotkey_linux_test.go b/internal/xhotkey/hotkey_linux_test.go new file mode 100644 index 0000000..7b7e24e --- /dev/null +++ b/internal/xhotkey/hotkey_linux_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build linux && cgo + +package hotkey_test + +import ( + "context" + "fmt" + "testing" + "time" + + "wis-free-v3/internal/xhotkey" +) + +// TestHotkey should always run success. +// This is a test to run and for manually testing, registered combination: +// Ctrl+Alt+A (Ctrl+Mod2+Mod4+A on Linux) +func TestHotkey(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + hk := hotkey.New([]hotkey.Modifier{ + hotkey.ModCtrl, hotkey.Mod2, hotkey.Mod4}, hotkey.KeyA) + if err := hk.Register(); err != nil { + t.Errorf("failed to register hotkey: %v", err) + return + } + for { + select { + case <-ctx.Done(): + return + case <-hk.Keydown(): + fmt.Println("triggered") + } + } +} diff --git a/internal/xhotkey/hotkey_nocgo.go b/internal/xhotkey/hotkey_nocgo.go new file mode 100644 index 0000000..ece8d80 --- /dev/null +++ b/internal/xhotkey/hotkey_nocgo.go @@ -0,0 +1,26 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build !windows && !cgo + +package hotkey + +type platformHotkey struct{} + +// Modifier represents a modifier +type Modifier uint32 + +// Key represents a key. +type Key uint8 + +func (hk *Hotkey) register() error { + panic("hotkey: cannot use when CGO_ENABLED=0") +} + +// unregister deregisteres a system hotkey. +func (hk *Hotkey) unregister() error { + panic("hotkey: cannot use when CGO_ENABLED=0") +} diff --git a/internal/xhotkey/hotkey_nocgo_test.go b/internal/xhotkey/hotkey_nocgo_test.go new file mode 100644 index 0000000..dc1f26a --- /dev/null +++ b/internal/xhotkey/hotkey_nocgo_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build (linux || darwin) && !cgo + +package hotkey_test + +import ( + "testing" + + "wis-free-v3/internal/xhotkey" +) + +// TestHotkey should always run success. +// This is a test to run and for manually testing, registered combination: +// Ctrl+Alt+A (Ctrl+Mod2+Mod4+A on Linux) +func TestHotkey(t *testing.T) { + defer func() { + if r := recover(); r != nil { + return + } + t.Fatalf("expect to fail when CGO_ENABLED=0") + }() + + hk := hotkey.New([]hotkey.Modifier{}, hotkey.Key(0)) + err := hk.Register() + if err != nil { + t.Fatal(err) + } + hk.Unregister() +} diff --git a/internal/xhotkey/hotkey_test.go b/internal/xhotkey/hotkey_test.go new file mode 100644 index 0000000..3127c40 --- /dev/null +++ b/internal/xhotkey/hotkey_test.go @@ -0,0 +1,20 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +package hotkey_test + +import ( + "os" + "testing" + + "wis-free-v3/internal/xhotkey/mainthread" +) + +// The test cannot be run twice since the mainthread loop may not be terminated: +// go test -v -count=1 +func TestMain(m *testing.M) { + mainthread.Init(func() { os.Exit(m.Run()) }) +} diff --git a/internal/xhotkey/hotkey_windows.go b/internal/xhotkey/hotkey_windows.go new file mode 100644 index 0000000..ce29f1d --- /dev/null +++ b/internal/xhotkey/hotkey_windows.go @@ -0,0 +1,226 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build windows + +package hotkey + +import ( + "errors" + "runtime" + "sync" + "sync/atomic" + "time" + + "wis-free-v3/internal/xhotkey/internal/win" +) + +type platformHotkey struct { + mu sync.Mutex + hotkeyId uint64 + registered bool + funcs chan func() + canceled chan struct{} +} + +var hotkeyId uint64 // atomic + +// register registers a system hotkey. It returns an error if +// the registration is failed. This could be that the hotkey is +// conflict with other hotkeys. +func (hk *Hotkey) register() error { + hk.mu.Lock() + if hk.registered { + hk.mu.Unlock() + return errors.New("hotkey already registered") + } + + mod := uint8(0) + for _, m := range hk.mods { + mod = mod | uint8(m) + } + + hk.hotkeyId = atomic.AddUint64(&hotkeyId, 1) + hk.funcs = make(chan func()) + hk.canceled = make(chan struct{}) + go hk.handle() + + var ( + ok bool + err error + done = make(chan struct{}) + ) + hk.funcs <- func() { + ok, err = win.RegisterHotKey(0, uintptr(hk.hotkeyId), uintptr(mod), uintptr(hk.key)) + done <- struct{}{} + } + <-done + if !ok { + close(hk.canceled) + hk.mu.Unlock() + return err + } + hk.registered = true + hk.mu.Unlock() + return nil +} + +// unregister deregisteres a system hotkey. +func (hk *Hotkey) unregister() error { + hk.mu.Lock() + defer hk.mu.Unlock() + if !hk.registered { + return errors.New("hotkey is not registered") + } + + done := make(chan struct{}) + hk.funcs <- func() { + win.UnregisterHotKey(0, uintptr(hk.hotkeyId)) + done <- struct{}{} + close(hk.canceled) + } + <-done + + <-hk.canceled + hk.registered = false + return nil +} + +const ( + // wmHotkey represents hotkey message + wmHotkey uint32 = 0x0312 + wmQuit uint32 = 0x0012 +) + +// handle handles the hotkey event loop. +func (hk *Hotkey) handle() { + // We could optimize this. So far each hotkey is served in an + // individual thread. If we have too many hotkeys, then a program + // have to create too many threads to serve them. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + tk := time.NewTicker(time.Second / 100) + for range tk.C { + msg := win.MSG{} + if !win.PeekMessage(&msg, 0, 0, 0) { + select { + case f := <-hk.funcs: + f() + case <-hk.canceled: + return + default: + } + continue + } + if !win.GetMessage(&msg, 0, 0, 0) { + return + } + + switch msg.Message { + case wmHotkey: + hk.keydownIn <- Event{} + + tk := time.NewTicker(time.Second / 100) + for range tk.C { + if win.GetAsyncKeyState(int(hk.key)) == 0 { + hk.keyupIn <- Event{} + break + } + } + case wmQuit: + return + } + } +} + +// Modifier represents a modifier. +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey +type Modifier uint8 + +// All kinds of Modifiers +const ( + ModAlt Modifier = 0x1 + ModCtrl Modifier = 0x2 + ModShift Modifier = 0x4 + ModWin Modifier = 0x8 +) + +// Key represents a key. +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +type Key uint16 + +// All kinds of Keys +const ( + KeySpace Key = 0x20 + Key0 Key = 0x30 + Key1 Key = 0x31 + Key2 Key = 0x32 + Key3 Key = 0x33 + Key4 Key = 0x34 + Key5 Key = 0x35 + Key6 Key = 0x36 + Key7 Key = 0x37 + Key8 Key = 0x38 + Key9 Key = 0x39 + KeyA Key = 0x41 + KeyB Key = 0x42 + KeyC Key = 0x43 + KeyD Key = 0x44 + KeyE Key = 0x45 + KeyF Key = 0x46 + KeyG Key = 0x47 + KeyH Key = 0x48 + KeyI Key = 0x49 + KeyJ Key = 0x4A + KeyK Key = 0x4B + KeyL Key = 0x4C + KeyM Key = 0x4D + KeyN Key = 0x4E + KeyO Key = 0x4F + KeyP Key = 0x50 + KeyQ Key = 0x51 + KeyR Key = 0x52 + KeyS Key = 0x53 + KeyT Key = 0x54 + KeyU Key = 0x55 + KeyV Key = 0x56 + KeyW Key = 0x57 + KeyX Key = 0x58 + KeyY Key = 0x59 + KeyZ Key = 0x5A + + KeyReturn Key = 0x0D + KeyEscape Key = 0x1B + KeyDelete Key = 0x2E + KeyTab Key = 0x09 + + KeyLeft Key = 0x25 + KeyRight Key = 0x27 + KeyUp Key = 0x26 + KeyDown Key = 0x28 + + KeyF1 Key = 0x70 + KeyF2 Key = 0x71 + KeyF3 Key = 0x72 + KeyF4 Key = 0x73 + KeyF5 Key = 0x74 + KeyF6 Key = 0x75 + KeyF7 Key = 0x76 + KeyF8 Key = 0x77 + KeyF9 Key = 0x78 + KeyF10 Key = 0x79 + KeyF11 Key = 0x7A + KeyF12 Key = 0x7B + KeyF13 Key = 0x7C + KeyF14 Key = 0x7D + KeyF15 Key = 0x7E + KeyF16 Key = 0x7F + KeyF17 Key = 0x80 + KeyF18 Key = 0x81 + KeyF19 Key = 0x82 + KeyF20 Key = 0x83 +) diff --git a/internal/xhotkey/hotkey_windows_test.go b/internal/xhotkey/hotkey_windows_test.go new file mode 100644 index 0000000..f0104da --- /dev/null +++ b/internal/xhotkey/hotkey_windows_test.go @@ -0,0 +1,42 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build windows + +package hotkey_test + +import ( + "context" + "fmt" + "testing" + "time" + + "wis-free-v3/internal/xhotkey" +) + +// TestHotkey should always run success. +// This is a test to run and for manually testing, registered combination: +// Ctrl+Shift+S +func TestHotkey(t *testing.T) { + tt := time.Second * 5 + + ctx, cancel := context.WithTimeout(context.Background(), tt) + defer cancel() + + hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS) + if err := hk.Register(); err != nil { + t.Errorf("failed to register hotkey: %v", err) + return + } + for { + select { + case <-ctx.Done(): + return + case <-hk.Keydown(): + fmt.Println("triggered") + } + } +} diff --git a/internal/xhotkey/internal/win/hotkey.go b/internal/xhotkey/internal/win/hotkey.go new file mode 100644 index 0000000..ad0ef73 --- /dev/null +++ b/internal/xhotkey/internal/win/hotkey.go @@ -0,0 +1,122 @@ +// Copyright 2021 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build windows +// +build windows + +package win + +import ( + "syscall" + "unsafe" +) + +var ( + user32 = syscall.NewLazyDLL("user32") + registerHotkey = user32.NewProc("RegisterHotKey") + unregisterHotkey = user32.NewProc("UnregisterHotKey") + getMessage = user32.NewProc("GetMessageW") + peekMessage = user32.NewProc("PeekMessageA") + sendMessage = user32.NewProc("SendMessageW") + getAsyncKeyState = user32.NewProc("GetAsyncKeyState") + quitMessage = user32.NewProc("PostQuitMessage") +) + +// RegisterHotKey defines a system-wide hot key. +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey +func RegisterHotKey(hwnd, id uintptr, mod uintptr, k uintptr) (bool, error) { + ret, _, err := registerHotkey.Call( + hwnd, id, mod, k, + ) + return ret != 0, err +} + +// UnregisterHotKey frees a hot key previously registered by the calling +// thread. +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unregisterhotkey +func UnregisterHotKey(hwnd, id uintptr) (bool, error) { + ret, _, err := unregisterHotkey.Call(hwnd, id) + return ret != 0, err +} + +// MSG contains message information from a thread's message queue. +// +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msg +type MSG struct { + HWnd uintptr + Message uint32 + WParam uintptr + LParam uintptr + Time uint32 + Pt struct { //POINT + x, y int32 + } +} + +// SendMessage sends the specified message to a window or windows. +// The SendMessage function calls the window procedure for the specified +// window and does not return until the window procedure has processed +// the message. +// The return value specifies the result of the message processing; +// it depends on the message sent. +// +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage +func SendMessage(hwnd uintptr, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := sendMessage.Call( + hwnd, + uintptr(msg), + wParam, + lParam, + ) + + return ret +} + +// GetMessage retrieves a message from the calling thread's message +// queue. The function dispatches incoming sent messages until a posted +// message is available for retrieval. +// +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage +func GetMessage(msg *MSG, hWnd uintptr, msgFilterMin, msgFilterMax uint32) bool { + ret, _, _ := getMessage.Call( + uintptr(unsafe.Pointer(msg)), + hWnd, + uintptr(msgFilterMin), + uintptr(msgFilterMax), + ) + + return ret != 0 +} + +// PeekMessage dispatches incoming sent messages, checks the thread message +// queue for a posted message, and retrieves the message (if any exist). +// +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-peekmessagea +func PeekMessage(msg *MSG, hWnd uintptr, msgFilterMin, msgFilterMax uint32) bool { + ret, _, _ := peekMessage.Call( + uintptr(unsafe.Pointer(msg)), + hWnd, + uintptr(msgFilterMin), + uintptr(msgFilterMax), + 0, // PM_NOREMOVE + ) + + return ret != 0 +} + +// PostQuitMessage indicates to the system that a thread has made +// a request to terminate (quit). It is typically used in response +// to a WM_DESTROY message. +// +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postquitmessage +func PostQuitMessage(exitCode int) { + quitMessage.Call(uintptr(exitCode)) +} + +func GetAsyncKeyState(keycode int) uintptr { + ret, _, _ := getAsyncKeyState.Call(uintptr(keycode)) + return ret +} diff --git a/internal/xhotkey/mainthread/doc.go b/internal/xhotkey/mainthread/doc.go new file mode 100644 index 0000000..7059908 --- /dev/null +++ b/internal/xhotkey/mainthread/doc.go @@ -0,0 +1,10 @@ +// Copyright 2022 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +// Package mainthread wrapps the golang.design/x/mainthread, and +// provides a different implementation for macOS so that it can +// handle main thread events for the NSApplication. +package mainthread diff --git a/internal/xhotkey/mainthread/os.go b/internal/xhotkey/mainthread/os.go new file mode 100644 index 0000000..605bf39 --- /dev/null +++ b/internal/xhotkey/mainthread/os.go @@ -0,0 +1,19 @@ +// Copyright 2022 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build windows || linux || (darwin && !cgo) + +package mainthread + +import "golang.design/x/mainthread" + +// Call calls f on the main thread and blocks until f finishes. +func Call(f func()) { mainthread.Call(f) } + +// Init initializes the functionality of running arbitrary subsequent functions be called on the main system thread. +// +// Init must be called in the main.main function. +func Init(main func()) { mainthread.Init(main) } diff --git a/internal/xhotkey/mainthread/os_darwin.go b/internal/xhotkey/mainthread/os_darwin.go new file mode 100644 index 0000000..90fbac4 --- /dev/null +++ b/internal/xhotkey/mainthread/os_darwin.go @@ -0,0 +1,69 @@ +// Copyright 2022 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build darwin + +package mainthread + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa +#import +#import + +extern void os_main(void); +extern void wakeupMainThread(void); +static bool isMainThread() { + return [NSThread isMainThread]; +} +*/ +import "C" +import ( + "os" + "runtime" +) + +func init() { + runtime.LockOSThread() +} + +// Call calls f on the main thread and blocks until f finishes. +func Call(f func()) { + if C.isMainThread() { + f() + return + } + go func() { + mainFuncs <- f + C.wakeupMainThread() + }() +} + +// Init initializes the functionality of running arbitrary subsequent functions be called on the main system thread. +// +// Init must be called in the main.main function. +func Init(f func()) { + go func() { + f() + os.Exit(0) + }() + + C.os_main() +} + +var mainFuncs = make(chan func(), 1) + +//export dispatchMainFuncs +func dispatchMainFuncs() { + for { + select { + case f := <-mainFuncs: + f() + default: + return + } + } +} diff --git a/internal/xhotkey/mainthread/os_darwin.m b/internal/xhotkey/mainthread/os_darwin.m new file mode 100644 index 0000000..31873b1 --- /dev/null +++ b/internal/xhotkey/mainthread/os_darwin.m @@ -0,0 +1,28 @@ +// Copyright 2022 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. +// +// Written by Changkun Ou + +//go:build darwin + +#include +#import + +extern void dispatchMainFuncs(); + +void wakeupMainThread(void) { + dispatch_async(dispatch_get_main_queue(), ^{ + dispatchMainFuncs(); + }); +} + +// The following three lines of code must run on the main thread. +// It must handle it using golang.design/x/mainthread. +// +// inspired from here: https://github.com/cehoffman/dotfiles/blob/4be8e893517e970d40746a9bdc67fe5832dd1c33/os/mac/iTerm2HotKey.m +void os_main(void) { + [NSApplication sharedApplication]; + [NSApp disableRelaunchOnLogin]; + [NSApp run]; +} \ No newline at end of file diff --git a/internal/xhotkey/vendor/modules.txt b/internal/xhotkey/vendor/modules.txt new file mode 100644 index 0000000..515297a --- /dev/null +++ b/internal/xhotkey/vendor/modules.txt @@ -0,0 +1,3 @@ +# golang.design/x/mainthread v0.3.0 +## explicit; go 1.16 +golang.design/x/mainthread diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh old mode 100644 new mode 100755 index 3d0dc19..515fdb0 --- a/scripts/build-linux.sh +++ b/scripts/build-linux.sh @@ -50,8 +50,8 @@ if command_exists pkg-config; then echo "[WARNING] Missing Wails dependencies (GTK3 / WebKit2GTK)." MISSING_DEPS=1 fi - if ! pkg-config --exists x11 xtst xcb; then - echo "[WARNING] Missing gohook dependencies (X11 / Xtst / Xcb)." + if ! pkg-config --exists x11 xtst xcb xkbcommon-x11; then + echo "[WARNING] Missing gohook dependencies (X11 / Xtst / Xcb / Xkbcommon)." MISSING_DEPS=1 fi if ! pkg-config --exists alsa; then @@ -92,6 +92,12 @@ fi # 3. Build the application echo "[2/3] Building with Wails..." + +# Pre-emptively fix npm bin permissions if they got messed up (common issue on some systems) +if [ -d "frontend/node_modules/.bin" ]; then + chmod +x frontend/node_modules/.bin/* 2>/dev/null || true +fi + wails build -platform linux/amd64 -clean if [ ! -f "$EXECUTABLE" ]; then diff --git a/scripts/tint_icon.go b/scripts/tint_icon.go new file mode 100644 index 0000000..e430664 --- /dev/null +++ b/scripts/tint_icon.go @@ -0,0 +1,67 @@ +package main + +import ( + "image" + "image/color" + "image/draw" + "image/png" + "os" +) + +func main() { + // Read original appicon + f, err := os.Open("build/appicon.png") + if err != nil { + panic(err) + } + defer f.Close() + + img, _, err := image.Decode(f) + if err != nil { + panic(err) + } + + bounds := img.Bounds() + + // Generic tint function + tint := func(name string, tintColor color.RGBA) { + out := image.NewRGBA(bounds) + // Draw original + draw.Draw(out, bounds, img, image.Point{}, draw.Src) + + // Draw tint over it using Atop or standard blending + // We'll just manually blend the pixels to preserve alpha + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + r1, g1, b1, a1 := out.At(x, y).RGBA() + if a1 == 0 { + continue + } + // Convert back to 8-bit + r, g, b, a := uint8(r1>>8), uint8(g1>>8), uint8(b1>>8), uint8(a1>>8) + + // Overlay tintColor with 50% opacity + // dst = (src * alpha + dst * (1-alpha)) + alpha := 0.5 + nr := uint8(float64(tintColor.R)*alpha + float64(r)*(1-alpha)) + ng := uint8(float64(tintColor.G)*alpha + float64(g)*(1-alpha)) + nb := uint8(float64(tintColor.B)*alpha + float64(b)*(1-alpha)) + + out.Set(x, y, color.RGBA{nr, ng, nb, a}) + } + } + + outF, err := os.Create("internal/ui/tray/" + name) + if err != nil { + panic(err) + } + defer outF.Close() + + png.Encode(outF, out) + } + + // Recording = Red + tint("icon_recording.png", color.RGBA{255, 0, 0, 255}) + // Transcribing = Blue + tint("icon_transcribing.png", color.RGBA{0, 150, 255, 255}) +}