feat: adiciona pré-compressão gzip no build

- Instala vite-plugin-compression2 para gerar .gz no build
- Configura Express para servir arquivos .gz quando disponível
- Reduz transferência em ~70% para assets principais

Agent.js: 33KB → 9.9KB (gzip)
index.js: 348KB → 108KB (gzip)
This commit is contained in:
Jonas Pacheco 2026-04-09 09:20:05 -03:00
parent 58efb8c95c
commit e27f2998e5
4 changed files with 102 additions and 25 deletions

51
package-lock.json generated
View File

@ -146,7 +146,8 @@
"tailwindcss": "^4.1.14",
"tsx": "^4.20.5",
"typescript": "5.6.3",
"vite": "^7.1.9"
"vite": "^7.1.9",
"vite-plugin-compression2": "^2.5.3"
},
"optionalDependencies": {
"bufferutil": "^4.0.8"
@ -5515,6 +5516,29 @@
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
@ -9660,6 +9684,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@ -13631,6 +13662,13 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/tar-mini": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/tar-mini/-/tar-mini-0.2.0.tgz",
"integrity": "sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==",
"dev": true,
"license": "MIT"
},
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
@ -14133,6 +14171,17 @@
}
}
},
"node_modules/vite-plugin-compression2": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/vite-plugin-compression2/-/vite-plugin-compression2-2.5.3.tgz",
"integrity": "sha512-ItPgqQWkcnBbVw7is9OKwiZ8v6+ju9rYROl5Lp6QfQDEx/d55AwJQb/KLpsQqsU9HoigYBsZ8tK6I02UwJNvEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.1.0",
"tar-mini": "^0.2.0"
}
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",

View File

@ -153,7 +153,8 @@
"tailwindcss": "^4.1.14",
"tsx": "^4.20.5",
"typescript": "5.6.3",
"vite": "^7.1.9"
"vite": "^7.1.9",
"vite-plugin-compression2": "^2.5.3"
},
"optionalDependencies": {
"bufferutil": "^4.0.8"

View File

@ -1,7 +1,6 @@
import express, { type Express } from "express";
import fs from "fs";
import path from "path";
import compression from "compression";
export function serveStatic(app: Express) {
const distPath = path.resolve(__dirname, "public");
@ -11,29 +10,48 @@ export function serveStatic(app: Express) {
);
}
// Enable gzip compression for static assets
// Caddy/Traefik devem respeitar isso ou fazer override
app.use(compression({
level: 6,
threshold: 1024, // Compress files > 1KB
filter: (req, res) => {
// Don't compress if already compressed by proxy
if (req.headers['x-no-compression']) {
return false;
// Middleware to serve pre-compressed .gz files
app.use((req, res, next) => {
const acceptEncoding = req.headers["accept-encoding"] || "";
// Only handle GET/HEAD requests
if (req.method !== "GET" && req.method !== "HEAD") {
return next();
}
// Check if client accepts gzip
if (!acceptEncoding.includes("gzip")) {
return next();
}
const url = req.url;
// Only handle JS, CSS, and HTML files
if (!url.match(/\.(js|css|html?)$/)) {
return next();
}
const gzPath = path.join(distPath, url + ".gz");
if (fs.existsSync(gzPath)) {
// Set headers for gzip response
res.setHeader("Content-Encoding", "gzip");
res.setHeader("Vary", "Accept-Encoding");
// Set correct content type
if (url.endsWith(".js")) {
res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
} else if (url.endsWith(".css")) {
res.setHeader("Content-Type", "text/css; charset=UTF-8");
} else if (url.match(/\.html?$/)) {
res.setHeader("Content-Type", "text/html; charset=UTF-8");
}
// Compress JS, CSS, JSON, HTML
const contentType = res.getHeader('content-type') || '';
if (typeof contentType === 'string') {
if (contentType.includes('javascript') ||
contentType.includes('css') ||
contentType.includes('json') ||
contentType.includes('html')) {
return true;
}
}
return compression.filter(req, res);
},
}));
// Serve the gzipped file
return res.sendFile(gzPath);
}
next();
});
// Serve static files with proper caching
app.use(express.static(distPath, {

View File

@ -4,6 +4,7 @@ import tailwindcss from "@tailwindcss/vite";
import path from "path";
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
import { metaImagesPlugin } from "./vite-plugin-meta-images";
import { compression } from "vite-plugin-compression2";
export default defineConfig({
plugins: [
@ -11,6 +12,14 @@ export default defineConfig({
runtimeErrorOverlay(),
tailwindcss(),
metaImagesPlugin(),
compression({
algorithm: "gzip",
exclude: [/\.(br)$/, /\.(gz)$/],
threshold: 1024,
compressionOptions: {
level: 6,
},
}),
...(process.env.NODE_ENV !== "production" &&
process.env.REPL_ID !== undefined
? [