mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-09-22 02:49:04 +02:00
fix: brotli compression ignored (#1661)
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,6 +22,21 @@ import (
|
||||
//go:embed all:dist/*
|
||||
var frontendFS embed.FS
|
||||
|
||||
// SvelteKit generates both gzip and Brotli sidecars for these extensions when precompress is enabled
|
||||
var precompressedExtensions = map[string]struct{}{
|
||||
".css": {},
|
||||
".html": {},
|
||||
".js": {},
|
||||
".json": {},
|
||||
".md": {},
|
||||
".mdx": {},
|
||||
".mjs": {},
|
||||
".svg": {},
|
||||
".txt": {},
|
||||
".wasm": {},
|
||||
".xml": {},
|
||||
}
|
||||
|
||||
// This function, created by the init() method, writes to "w" the index.html page, populating the nonce
|
||||
var writeIndexFn func(w io.Writer, nonce string) error
|
||||
|
||||
@@ -59,14 +75,8 @@ func RegisterFrontend(router *gin.Engine) error {
|
||||
return fmt.Errorf("failed to create sub FS: %w", err)
|
||||
}
|
||||
|
||||
// Load a map of all files to see which ones are available pre-compressed
|
||||
preCompressed, err := listPreCompressedAssets(distFS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to index pre-compressed frontend assets: %w", err)
|
||||
}
|
||||
|
||||
// Init the file server
|
||||
fileServer := NewFileServerWithCaching(http.FS(distFS), preCompressed)
|
||||
fileServer := NewFileServerWithCaching(http.FS(distFS))
|
||||
|
||||
// Handler for Gin
|
||||
handler := func(c *gin.Context) {
|
||||
@@ -123,15 +133,13 @@ type FileServerWithCaching struct {
|
||||
root http.FileSystem
|
||||
lastModified time.Time
|
||||
lastModifiedHeaderValue string
|
||||
preCompressed preCompressedMap
|
||||
}
|
||||
|
||||
func NewFileServerWithCaching(root http.FileSystem, preCompressed preCompressedMap) *FileServerWithCaching {
|
||||
func NewFileServerWithCaching(root http.FileSystem) *FileServerWithCaching {
|
||||
return &FileServerWithCaching{
|
||||
root: root,
|
||||
lastModified: time.Now(),
|
||||
lastModifiedHeaderValue: time.Now().UTC().Format(http.TimeFormat),
|
||||
preCompressed: preCompressed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +166,14 @@ func (f *FileServerWithCaching) ServeHTTP(w http.ResponseWriter, r *http.Request
|
||||
w.Header().Set("Cache-Control", "public, max-age=86400")
|
||||
}
|
||||
|
||||
// Check if the asset is available pre-compressed
|
||||
_, ok := f.preCompressed[r.URL.Path]
|
||||
// SvelteKit creates both sidecars for every asset with a precompressed extension
|
||||
_, ok := precompressedExtensions[path.Ext(r.URL.Path)]
|
||||
if ok {
|
||||
// Add a "Vary" with "Accept-Encoding" so CDNs are aware that content is pre-compressed
|
||||
w.Header().Add("Vary", "Accept-Encoding")
|
||||
|
||||
// Select the encoding if any
|
||||
ext, ce := f.selectEncoding(r)
|
||||
ext, ce := selectEncoding(r)
|
||||
if ext != "" {
|
||||
// Set the content type explicitly before changing the path
|
||||
ct := mime.TypeByExtension(path.Ext(r.URL.Path))
|
||||
@@ -182,23 +190,58 @@ func (f *FileServerWithCaching) ServeHTTP(w http.ResponseWriter, r *http.Request
|
||||
http.FileServer(f.root).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (f *FileServerWithCaching) selectEncoding(r *http.Request) (ext string, contentEnc string) {
|
||||
available, ok := f.preCompressed[r.URL.Path]
|
||||
if !ok {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Check if the client accepts compressed files
|
||||
acceptEncoding := strings.TrimSpace(strings.ToLower(r.Header.Get("Accept-Encoding")))
|
||||
func selectEncoding(r *http.Request) (ext string, contentEnc string) {
|
||||
// Check which available encoding the client prefers
|
||||
acceptEncoding := r.Header.Get("Accept-Encoding")
|
||||
if acceptEncoding == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Prefer brotli over gzip when both are accepted.
|
||||
if available.br && (acceptEncoding == "*" || acceptEncoding == "br" || strings.Contains(acceptEncoding, "br")) {
|
||||
// Header can have multiple encodings with optional quality values, e.g. "gzip;q=1.0, br;q=0.8, *;q=0.5"
|
||||
brWeight, gzipWeight, wildcardWeight := -1.0, -1.0, -1.0
|
||||
for part := range strings.SplitSeq(acceptEncoding, ",") {
|
||||
codingAndParams := strings.Split(part, ";")
|
||||
coding := strings.ToLower(strings.TrimSpace(codingAndParams[0]))
|
||||
weight := 1.0
|
||||
|
||||
for _, param := range codingAndParams[1:] {
|
||||
key, value, found := strings.Cut(param, "=")
|
||||
if !found || !strings.EqualFold(strings.TrimSpace(key), "q") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the quality value
|
||||
parsed, err := strconv.ParseFloat(strings.TrimSpace(value), 64)
|
||||
if err != nil || parsed < 0 || parsed > 1 {
|
||||
weight = 0
|
||||
break
|
||||
}
|
||||
weight = parsed
|
||||
}
|
||||
|
||||
switch coding {
|
||||
case "br":
|
||||
brWeight = weight
|
||||
case "gzip":
|
||||
gzipWeight = weight
|
||||
case "*":
|
||||
wildcardWeight = weight
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the wildcard only to encodings the client did not name explicitly
|
||||
if brWeight < 0 {
|
||||
brWeight = wildcardWeight
|
||||
}
|
||||
if gzipWeight < 0 {
|
||||
gzipWeight = wildcardWeight
|
||||
}
|
||||
|
||||
// Prefer brotli when both available encodings have the same quality
|
||||
if brWeight > 0 && brWeight >= gzipWeight {
|
||||
return "br", "br"
|
||||
}
|
||||
if available.gz && (acceptEncoding == "gzip" || strings.Contains(acceptEncoding, "gzip")) {
|
||||
if gzipWeight > 0 {
|
||||
return "gz", "gzip"
|
||||
}
|
||||
|
||||
@@ -219,41 +262,3 @@ func isImmutableAsset(r *http.Request) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type preCompressedMap map[string]struct {
|
||||
br bool
|
||||
gz bool
|
||||
}
|
||||
|
||||
func listPreCompressedAssets(distFS fs.FS) (preCompressedMap, error) {
|
||||
preCompressed := make(preCompressedMap, 0)
|
||||
err := fs.WalkDir(distFS, ".", func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(path, ".br"):
|
||||
originalPath := "/" + strings.TrimSuffix(path, ".br")
|
||||
entry := preCompressed[originalPath]
|
||||
entry.br = true
|
||||
preCompressed[originalPath] = entry
|
||||
case strings.HasSuffix(path, ".gz"):
|
||||
originalPath := "/" + strings.TrimSuffix(path, ".gz")
|
||||
entry := preCompressed[originalPath]
|
||||
entry.gz = true
|
||||
preCompressed[originalPath] = entry
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return preCompressed, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
@@ -26,3 +28,85 @@ func TestIsSPARequest(t *testing.T) {
|
||||
assert.True(t, isSPARequest("authorize", distFS))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFileServerWithCachingServesPrecompressedAssets(t *testing.T) {
|
||||
distFS := fstest.MapFS{
|
||||
"assets/app.js": &fstest.MapFile{Data: []byte("original")},
|
||||
"assets/app.js.br": &fstest.MapFile{Data: []byte("brotli")},
|
||||
"assets/app.js.gz": &fstest.MapFile{Data: []byte("gzip")},
|
||||
}
|
||||
|
||||
fileServer := NewFileServerWithCaching(http.FS(distFS))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
acceptEncoding string
|
||||
expectedBody string
|
||||
contentEncoding string
|
||||
}{
|
||||
{
|
||||
name: "serves brotli when accepted",
|
||||
acceptEncoding: "gzip, deflate, br",
|
||||
expectedBody: "brotli",
|
||||
contentEncoding: "br",
|
||||
},
|
||||
{
|
||||
name: "serves gzip when brotli is not accepted",
|
||||
acceptEncoding: "gzip",
|
||||
expectedBody: "gzip",
|
||||
contentEncoding: "gzip",
|
||||
},
|
||||
{
|
||||
name: "honors encoding quality",
|
||||
acceptEncoding: "br;q=0.5, gzip;q=1",
|
||||
expectedBody: "gzip",
|
||||
contentEncoding: "gzip",
|
||||
},
|
||||
{
|
||||
name: "does not serve a rejected encoding",
|
||||
acceptEncoding: "br;q=0, gzip;q=0",
|
||||
expectedBody: "original",
|
||||
contentEncoding: "",
|
||||
},
|
||||
{
|
||||
name: "serves the original without accept encoding",
|
||||
acceptEncoding: "",
|
||||
expectedBody: "original",
|
||||
contentEncoding: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/assets/app.js", nil)
|
||||
req.Header.Set("Accept-Encoding", tt.acceptEncoding)
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
fileServer.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.Code)
|
||||
assert.Equal(t, tt.expectedBody, res.Body.String())
|
||||
assert.Equal(t, tt.contentEncoding, res.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "Accept-Encoding", res.Header().Get("Vary"))
|
||||
assert.Equal(t, "text/javascript; charset=utf-8", res.Header().Get("Content-Type"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileServerWithCachingDoesNotCompressUnsupportedExtensions(t *testing.T) {
|
||||
distFS := fstest.MapFS{
|
||||
"assets/font.woff2": &fstest.MapFile{Data: []byte("font")},
|
||||
"assets/font.woff2.br": &fstest.MapFile{Data: []byte("unused")},
|
||||
}
|
||||
fileServer := NewFileServerWithCaching(http.FS(distFS))
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/assets/font.woff2", nil)
|
||||
req.Header.Set("Accept-Encoding", "br")
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
fileServer.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.Code)
|
||||
assert.Equal(t, "font", res.Body.String())
|
||||
assert.Empty(t, res.Header().Get("Content-Encoding"))
|
||||
assert.Empty(t, res.Header().Get("Vary"))
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.4",
|
||||
"vite": "^8.0.16",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
"vite": "^8.0.16"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ const config = {
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter({
|
||||
fallback: 'index.html',
|
||||
pages: process.env.BUILD_OUTPUT_PATH ?? '../backend/frontend/dist'
|
||||
pages: process.env.BUILD_OUTPUT_PATH ?? '../backend/frontend/dist',
|
||||
precompress: true
|
||||
}),
|
||||
version: {
|
||||
name: packageJson.version
|
||||
|
||||
+1
-18
@@ -2,9 +2,8 @@ import { paraglideVitePlugin } from '@inlang/paraglide-js';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import viteCompression from 'vite-plugin-compression';
|
||||
|
||||
export default defineConfig((mode) => {
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
@@ -15,22 +14,6 @@ export default defineConfig((mode) => {
|
||||
emitTsDeclarations: true,
|
||||
cookieName: 'locale',
|
||||
strategy: ['cookie', 'preferredLanguage', 'baseLocale']
|
||||
}),
|
||||
|
||||
// Create gzip-compressed files
|
||||
viteCompression({
|
||||
disable: mode.isPreview,
|
||||
algorithm: 'gzip',
|
||||
ext: '.gz',
|
||||
filter: /\.(js|mjs|json|css)$/i
|
||||
}),
|
||||
|
||||
// Create brotli-compressed files
|
||||
viteCompression({
|
||||
disable: mode.isPreview,
|
||||
algorithm: 'brotliCompress',
|
||||
ext: '.br',
|
||||
filter: /\.(js|mjs|json|css)$/i
|
||||
})
|
||||
],
|
||||
|
||||
|
||||
Generated
-65
@@ -189,9 +189,6 @@ importers:
|
||||
vite:
|
||||
specifier: ^8.0.16
|
||||
version: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
vite-plugin-compression:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))
|
||||
|
||||
tests:
|
||||
dependencies:
|
||||
@@ -1576,10 +1573,6 @@ packages:
|
||||
caniuse-lite@1.0.30001800:
|
||||
resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==}
|
||||
|
||||
chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
@@ -1960,10 +1953,6 @@ packages:
|
||||
svelte: ^5.0.0
|
||||
sveltekit-superforms: ^2.19.0
|
||||
|
||||
fs-extra@10.1.0:
|
||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@@ -2012,10 +2001,6 @@ packages:
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
has-symbols@1.1.0:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2132,9 +2117,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
jsonfile@6.2.1:
|
||||
resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==}
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
@@ -2853,10 +2835,6 @@ packages:
|
||||
resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
svelte-check@4.7.3:
|
||||
resolution: {integrity: sha512-DHdTCGX62R0fCxBEaT+USdASAnoaRBaaNczkRJl0K7o3WyoCeVUbVxo6fKqpOll/B+WMWCsiFK0eFrJSNBKZIg==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
@@ -3014,10 +2992,6 @@ packages:
|
||||
undici-types@7.24.6:
|
||||
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
||||
|
||||
universalify@2.0.1:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
unplugin@2.3.11:
|
||||
resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
@@ -3051,11 +3025,6 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
vite-plugin-compression@0.5.1:
|
||||
resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
|
||||
peerDependencies:
|
||||
vite: '>=2.0.0'
|
||||
|
||||
vite@8.1.5:
|
||||
resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -4352,11 +4321,6 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001800: {}
|
||||
|
||||
chalk@4.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
@@ -4756,12 +4720,6 @@ snapshots:
|
||||
svelte-toolbelt: 0.5.0(svelte@5.56.7(@typescript-eslint/types@8.65.0))
|
||||
sveltekit-superforms: 2.30.2(@sveltejs/kit@2.70.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.7(@typescript-eslint/types@8.65.0))(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)))(svelte@5.56.7(@typescript-eslint/types@8.65.0))(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.7(@typescript-eslint/types@8.65.0))(typescript@6.0.3)
|
||||
|
||||
fs-extra@10.1.0:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 6.2.1
|
||||
universalify: 2.0.1
|
||||
|
||||
fsevents@2.3.2:
|
||||
optional: true
|
||||
|
||||
@@ -4808,8 +4766,6 @@ snapshots:
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
@@ -4909,12 +4865,6 @@ snapshots:
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsonfile@6.2.1:
|
||||
dependencies:
|
||||
universalify: 2.0.1
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
@@ -5533,10 +5483,6 @@ snapshots:
|
||||
superstruct@2.0.2:
|
||||
optional: true
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
svelte-check@4.7.3(picomatch@4.0.5)(svelte@5.56.7(@typescript-eslint/types@8.65.0))(typescript@6.0.3):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
@@ -5725,8 +5671,6 @@ snapshots:
|
||||
|
||||
undici-types@7.24.6: {}
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unplugin@2.3.11:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
@@ -5754,15 +5698,6 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-plugin-compression@0.5.1(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)):
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.3
|
||||
fs-extra: 10.1.0
|
||||
vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0):
|
||||
dependencies:
|
||||
lightningcss: 1.33.0
|
||||
|
||||
Reference in New Issue
Block a user