From 840c6cdebaaa0a972af38d47c10d3f07818e3e62 Mon Sep 17 00:00:00 2001 From: mifi Date: Sat, 7 Feb 2026 00:30:08 -0300 Subject: [PATCH] Minification and compression --- .woodpecker/deploy.yml | 6 +- Dockerfile | 1 + nginx.conf | 49 ++++ package.json | 13 +- pnpm-lock.yaml | 352 ++++++++++++++++++++++++++ scripts/externalize-inline-script.mjs | 65 +++-- scripts/minify-static-assets.mjs | 41 +++ static/assets/js/bootstrap.js | 10 +- vite.config.ts | 3 + 9 files changed, 501 insertions(+), 39 deletions(-) create mode 100644 nginx.conf create mode 100644 scripts/minify-static-assets.mjs diff --git a/.woodpecker/deploy.yml b/.woodpecker/deploy.yml index b002985..e8cd49d 100644 --- a/.woodpecker/deploy.yml +++ b/.woodpecker/deploy.yml @@ -14,8 +14,8 @@ steps: image: docker:latest environment: REGISTRY_REPO: git.mifi.dev/mifi-holdings/mifi-links - DOCKER_API_VERSION: "1.43" - DOCKER_BUILDKIT: "1" + DOCKER_API_VERSION: '1.43' + DOCKER_BUILDKIT: '1' volumes: - /var/run/docker.sock:/var/run/docker.sock commands: @@ -48,7 +48,7 @@ steps: - name: Push to registry image: docker:latest environment: - DOCKER_API_VERSION: "1.43" + DOCKER_API_VERSION: '1.43' REGISTRY_URL: git.mifi.dev REGISTRY_REPO: git.mifi.dev/mifi-holdings/mifi-links REGISTRY_USERNAME: diff --git a/Dockerfile b/Dockerfile index dfdbe64..65e00b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ FROM nginx:alpine COPY --from=builder /out/dev /usr/share/nginx/html/dev COPY --from=builder /out/bio /usr/share/nginx/html/bio +COPY nginx.conf /etc/nginx/nginx.conf COPY nginx/default.conf /etc/nginx/conf.d/default.conf COPY nginx/snippets/ /etc/nginx/snippets/ diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..d63bc1d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,49 @@ + Minimal nginx configuration for static site delivery +# Security headers are handled upstream by Traefik + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + + # Performance optimizations + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + # Gzip compression for text-based assets + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + # text/html is always gzipped by default; listing it again causes "duplicate MIME type" warning + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/rss+xml + application/atom+xml + image/svg+xml; +} diff --git a/package.json b/package.json index 33d0d97..f214d9c 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,12 @@ "scripts": { "dev:bio": "CONTENT_VARIANT=bio vite dev", "dev:dev": "CONTENT_VARIANT=dev vite dev", - "build": "vite build && node scripts/externalize-inline-script.mjs", - "build:bio": "CONTENT_VARIANT=bio vite build && node scripts/externalize-inline-script.mjs", - "build:dev": "CONTENT_VARIANT=dev vite build && node scripts/externalize-inline-script.mjs", - "build:full": "vite build && pnpm run critical-css && node scripts/externalize-inline-script.mjs", - "build:full:bio": "CONTENT_VARIANT=bio vite build && pnpm run critical-css && node scripts/externalize-inline-script.mjs", - "build:full:dev": "CONTENT_VARIANT=dev vite build && pnpm run critical-css && node scripts/externalize-inline-script.mjs", + "build": "vite build && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", + "build:bio": "CONTENT_VARIANT=bio vite build && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", + "build:dev": "CONTENT_VARIANT=dev vite build && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", + "build:full": "vite build && pnpm run critical-css && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", + "build:full:bio": "CONTENT_VARIANT=bio vite build && pnpm run critical-css && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", + "build:full:dev": "CONTENT_VARIANT=dev vite build && pnpm run critical-css && node scripts/minify-static-assets.mjs && node scripts/externalize-inline-script.mjs", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", @@ -40,6 +40,7 @@ "@typescript-eslint/parser": "^8.16.0", "@vitest/coverage-v8": "^2.1.0", "critical": "^7.0.0", + "esbuild": "^0.27.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09dfaaf..0efc5a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: critical: specifier: ^7.0.0 version: 7.2.1 + esbuild: + specifier: ^0.27.3 + version: 0.27.3 eslint: specifier: ^8.57.0 version: 8.57.1 @@ -721,6 +724,15 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.3': + resolution: + { + integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: { @@ -739,6 +751,15 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.3': + resolution: + { + integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: { @@ -757,6 +778,15 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.3': + resolution: + { + integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: { @@ -775,6 +805,15 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.3': + resolution: + { + integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: { @@ -793,6 +832,15 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.3': + resolution: + { + integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: { @@ -811,6 +859,15 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.3': + resolution: + { + integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: { @@ -829,6 +886,15 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.3': + resolution: + { + integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: { @@ -847,6 +913,15 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': + resolution: + { + integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: { @@ -865,6 +940,15 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.3': + resolution: + { + integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: { @@ -883,6 +967,15 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.3': + resolution: + { + integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: { @@ -901,6 +994,15 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.3': + resolution: + { + integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: { @@ -919,6 +1021,15 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.3': + resolution: + { + integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, + } + engines: { node: '>=18' } + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: { @@ -937,6 +1048,15 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.3': + resolution: + { + integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, + } + engines: { node: '>=18' } + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: { @@ -955,6 +1075,15 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.3': + resolution: + { + integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: { @@ -973,6 +1102,15 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.3': + resolution: + { + integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, + } + engines: { node: '>=18' } + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: { @@ -991,6 +1129,15 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.3': + resolution: + { + integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, + } + engines: { node: '>=18' } + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: { @@ -1009,6 +1156,15 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.3': + resolution: + { + integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: { @@ -1018,6 +1174,15 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.3': + resolution: + { + integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: { @@ -1036,6 +1201,15 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': + resolution: + { + integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: { @@ -1045,6 +1219,15 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.3': + resolution: + { + integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: { @@ -1063,6 +1246,15 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': + resolution: + { + integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: { @@ -1072,6 +1264,15 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.3': + resolution: + { + integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.21.5': resolution: { @@ -1090,6 +1291,15 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.3': + resolution: + { + integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: { @@ -1108,6 +1318,15 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.3': + resolution: + { + integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: { @@ -1126,6 +1345,15 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.3': + resolution: + { + integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: { @@ -1144,6 +1372,15 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.3': + resolution: + { + integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.1': resolution: { @@ -2885,6 +3122,14 @@ packages: engines: { node: '>=18' } hasBin: true + esbuild@0.27.3: + resolution: + { + integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, + } + engines: { node: '>=18' } + hasBin: true + escalade@3.2.0: resolution: { @@ -6678,147 +6923,225 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/aix-ppc64@0.27.3': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm64@0.27.3': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-arm@0.27.3': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/android-x64@0.27.3': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.27.3': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/darwin-x64@0.27.3': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.27.3': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.27.3': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm64@0.27.3': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-arm@0.27.3': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-ia32@0.27.3': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-loong64@0.27.3': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-mips64el@0.27.3': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-ppc64@0.27.3': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.27.3': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-s390x@0.27.3': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.25.12': optional: true + '@esbuild/linux-x64@0.27.3': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-arm64@0.27.3': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.27.3': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-arm64@0.27.3': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.27.3': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/openharmony-arm64@0.27.3': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/sunos-x64@0.27.3': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-arm64@0.27.3': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-ia32@0.27.3': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.25.12': optional: true + '@esbuild/win32-x64@0.27.3': + optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -7904,6 +8227,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} diff --git a/scripts/externalize-inline-script.mjs b/scripts/externalize-inline-script.mjs index a986dea..5180f3c 100644 --- a/scripts/externalize-inline-script.mjs +++ b/scripts/externalize-inline-script.mjs @@ -1,6 +1,7 @@ /** * Post-build: extract SvelteKit's inline bootstrap script to an external file * and replace it with `; + html = html.slice(0, found.start) + scriptTag + html.slice(found.end); + // Use absolute paths for _app assets so they resolve correctly (host-based routing, redirects) + html = html.replace(/\.\/_app\//g, '/_app/'); + writeFileSync(htmlPath, html, 'utf-8'); + console.log('Externalized SvelteKit bootstrap to', scriptPath); + } catch (err) { + console.error( + 'externalize-inline-script failed:', + err instanceof Error ? err.message : String(err), + ); + process.exit(1); } - let content = found.content; - // Bootstrap runs from /_app/immutable/bootstrap.xxx.js; imports like "./_app/immutable/entry/..." - // would resolve to /_app/immutable/_app/immutable/entry/... (duplicate). Use directory-relative paths. - content = content.replace(/\.\/_app\/immutable\//g, './'); - const hash = createHash('sha256').update(content).digest('hex').slice(0, 8); - const filename = `bootstrap.${hash}.js`; - const immutableDir = join(buildDir, '_app', 'immutable'); - mkdirSync(immutableDir, { recursive: true }); - const scriptPath = join(immutableDir, filename); - writeFileSync(scriptPath, content.trimStart(), 'utf-8'); - const scriptTag = ``; - html = html.slice(0, found.start) + scriptTag + html.slice(found.end); - // Use absolute paths for _app assets so they resolve correctly (host-based routing, redirects) - html = html.replace(/\.\/_app\//g, '/_app/'); - writeFileSync(htmlPath, html, 'utf-8'); - console.log('Externalized SvelteKit bootstrap to', scriptPath); -} catch (err) { - console.error( - 'externalize-inline-script failed:', - err instanceof Error ? err.message : String(err), - ); - process.exit(1); } + +main(); diff --git a/scripts/minify-static-assets.mjs b/scripts/minify-static-assets.mjs new file mode 100644 index 0000000..fe0738c --- /dev/null +++ b/scripts/minify-static-assets.mjs @@ -0,0 +1,41 @@ +/** + * Post-build: minify JS and CSS under build/assets (files copied from static/). + * Usage: node scripts/minify-static-assets.mjs [buildDir] + */ +import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { cwd } from 'node:process'; +import * as esbuild from 'esbuild'; + +const buildDir = join(cwd(), process.argv[2] || 'build'); +const assetsDir = join(buildDir, 'assets'); + +function* walk(dir, base = '') { + for (const name of readdirSync(dir, { withFileTypes: true })) { + const rel = join(base, name.name); + if (name.isDirectory()) yield* walk(join(dir, name.name), rel); + else yield rel; + } +} + +async function minifyAll() { + if (!existsSync(assetsDir)) return; + for (const rel of walk(assetsDir, '')) { + const path = join(assetsDir, rel); + const ext = rel.slice(rel.lastIndexOf('.')); + if (ext === '.js') { + const code = readFileSync(path, 'utf-8'); + const out = await esbuild.transform(code, { minify: true, loader: 'js' }); + writeFileSync(path, out.code); + } else if (ext === '.css') { + const code = readFileSync(path, 'utf-8'); + const out = await esbuild.transform(code, { minify: true, loader: 'css' }); + writeFileSync(path, out.code); + } + } +} + +minifyAll().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/static/assets/js/bootstrap.js b/static/assets/js/bootstrap.js index 372251d..ce0dea3 100644 --- a/static/assets/js/bootstrap.js +++ b/static/assets/js/bootstrap.js @@ -2,8 +2,14 @@ // Default Trusted Type policy so Svelte hydration can assign to innerHTML under CSP require-trusted-types-for 'script'. if (typeof window.trustedTypes !== 'undefined' && window.trustedTypes.createPolicy) { try { - window.trustedTypes.createPolicy('default', { createHTML: function (input) { return input; } }); - } catch { /* policy already exists */ } + window.trustedTypes.createPolicy('default', { + createHTML: function (input) { + return input; + }, + }); + } catch { + /* policy already exists */ + } } var t = localStorage.getItem('mifi-theme'); diff --git a/vite.config.ts b/vite.config.ts index b109c7d..732c1a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,4 +3,7 @@ import { defineConfig } from 'vite'; export default defineConfig({ plugins: [sveltekit()], + build: { + cssMinify: true, + }, });