-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Improve Lighthouse Scrore #6296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2ec0b7a
8ca31a5
cb3e850
40d5aa7
3f6f2a0
cacc11b
11aae47
10d0893
a633787
56fe0f2
195986b
522bcc0
6f9543f
dd6803e
fa39149
31820ec
d8c9bea
cd16bf4
8322ae3
8c7baca
fd0d87c
f8e1eb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,26 +11,37 @@ class State(rx.State): | |
|
|
||
| def index() -> rx.Component: | ||
| # Welcome Page (Index) | ||
| return rx.container( | ||
| rx.color_mode.button(position="top-right"), | ||
| rx.vstack( | ||
| rx.heading("Welcome to Reflex!", size="9"), | ||
| rx.text( | ||
| "Get started by editing ", | ||
| rx.code(f"{config.app_name}/{config.app_name}.py"), | ||
| size="5", | ||
| return rx.el.main( | ||
| rx.container( | ||
| rx.color_mode.button(position="top-right"), | ||
| rx.vstack( | ||
| rx.heading("Welcome to Reflex!", size="9"), | ||
| rx.text( | ||
| "Get started by editing ", | ||
| rx.code(f"{config.app_name}/{config.app_name}.py"), | ||
| size="5", | ||
| ), | ||
| rx.button( | ||
| rx.link( | ||
| "Check out our docs!", | ||
| href="https://reflex.dev/docs/getting-started/introduction/", | ||
| is_external=True, | ||
| underline="none", | ||
| ), | ||
| as_child=True, | ||
| high_contrast=True, | ||
|
Comment on lines
+29
to
+32
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do these changes actually affect the lighthouse score? |
||
| ), | ||
| spacing="5", | ||
| justify="center", | ||
| min_height="85vh", | ||
| ), | ||
| rx.link( | ||
| rx.button("Check out our docs!"), | ||
| href="https://reflex.dev/docs/getting-started/introduction/", | ||
| is_external=True, | ||
| ), | ||
| spacing="5", | ||
| justify="center", | ||
| min_height="85vh", | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| app = rx.App() | ||
| app.add_page(index) | ||
| app.add_page( | ||
| index, | ||
| title="Welcome to Reflex", | ||
| description="A starter Reflex app.", | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { compressDirectory } from "./vite-plugin-compress.js"; | ||
|
|
||
| const [directory, formatsArg = "[]"] = process.argv.slice(2); | ||
|
|
||
| if (!directory) { | ||
| throw new Error("Missing static output directory for compression."); | ||
| } | ||
|
|
||
| await compressDirectory(directory, JSON.parse(formatsArg)); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,3 @@ | ||
| import JSON5 from "json5"; | ||
| import env from "$/env.json"; | ||
|
|
||
| /** | ||
|
|
@@ -45,7 +44,7 @@ export const uploadFiles = async ( | |
| // So only process _new_ chunks beyond resp_idx. | ||
| chunks.slice(resp_idx).map((chunk_json) => { | ||
| try { | ||
| const chunk = JSON5.parse(chunk_json); | ||
| const chunk = JSON.parse(chunk_json); | ||
|
Comment on lines
-48
to
+47
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this reuse the parsing method that the main websocket dispatch loop is using? these chunks could be anything from the backend, including weird floats |
||
| event_callbacks.map((f, ix) => { | ||
| f(chunk) | ||
| .then(() => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /* vite-plugin-compress.js | ||
| * | ||
| * Generate pre-compressed build assets so they can be served directly by | ||
| * production static file servers and reverse proxies without on-the-fly | ||
| * compression. The default format is gzip, with optional brotli and zstd. | ||
| */ | ||
|
|
||
| import * as zlib from "node:zlib"; | ||
| import { dirname } from "node:path"; | ||
| import { access, readFile, writeFile } from "node:fs/promises"; | ||
| import { promisify } from "node:util"; | ||
| import { | ||
| validateFormats, | ||
| outputDirectoryExists, | ||
| walkFiles, | ||
| } from "./vite-plugin-utils.js"; | ||
|
|
||
| const gzipAsync = promisify(zlib.gzip); | ||
| const brotliAsync = | ||
| typeof zlib.brotliCompress === "function" | ||
| ? promisify(zlib.brotliCompress) | ||
| : null; | ||
| const zstdAsync = | ||
| typeof zlib.zstdCompress === "function" ? promisify(zlib.zstdCompress) : null; | ||
|
|
||
| const COMPRESSIBLE_EXTENSIONS = /\.(js|css|html|json|svg|xml|txt|map|mjs)$/; | ||
|
|
||
| // Only compress files above this size (bytes). Tiny assets rarely benefit, | ||
| // but HTML entrypoints are always compressed so their negotiated sidecars exist. | ||
| const MIN_SIZE = 256; | ||
|
|
||
| const COMPRESSORS = { | ||
| gzip: { | ||
| extension: ".gz", | ||
| compress: (raw) => gzipAsync(raw, { level: 9 }), | ||
| }, | ||
| brotli: brotliAsync && { | ||
| extension: ".br", | ||
| compress: (raw) => | ||
| brotliAsync(raw, { | ||
| params: { | ||
| [zlib.constants.BROTLI_PARAM_QUALITY]: | ||
| zlib.constants.BROTLI_MAX_QUALITY ?? 11, | ||
| }, | ||
| }), | ||
| }, | ||
| zstd: zstdAsync && { | ||
| extension: ".zst", | ||
| compress: (raw) => zstdAsync(raw), | ||
| }, | ||
| }; | ||
|
|
||
| // Concurrency limit for parallel file compression. | ||
| const CONCURRENCY = 16; | ||
|
|
||
| function ensureFormatsSupported(formats) { | ||
| const unavailableFormats = formats.filter( | ||
| (format) => !COMPRESSORS[format]?.compress, | ||
| ); | ||
| if (unavailableFormats.length > 0) { | ||
| throw new Error( | ||
| `The configured frontend compression formats are not supported by this Node.js runtime: ${unavailableFormats.join(", ")}`, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| async function compressFile(filePath, formats) { | ||
| const pendingFormats = []; | ||
| for (const format of formats) { | ||
| const compressor = COMPRESSORS[format]; | ||
| try { | ||
| await access(filePath + compressor.extension); | ||
| } catch { | ||
| pendingFormats.push([format, compressor]); | ||
| } | ||
| } | ||
|
|
||
| if (pendingFormats.length === 0) return; | ||
|
|
||
| const raw = await readFile(filePath); | ||
| if (raw.length < MIN_SIZE && !filePath.endsWith(".html")) return; | ||
|
|
||
| await Promise.all( | ||
| pendingFormats.map(([_format, compressor]) => { | ||
| return compressor | ||
| .compress(raw) | ||
| .then((compressed) => | ||
| writeFile(filePath + compressor.extension, compressed), | ||
| ); | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| export async function compressDirectory(directory, formats = ["gzip"]) { | ||
| validateFormats(formats, COMPRESSORS, "frontend compression format"); | ||
| ensureFormatsSupported(formats); | ||
|
|
||
| if (!(await outputDirectoryExists(directory))) { | ||
| return; | ||
| } | ||
|
|
||
| const pending = []; | ||
| for await (const filePath of walkFiles(directory)) { | ||
| if (!COMPRESSIBLE_EXTENSIONS.test(filePath)) continue; | ||
| pending.push(filePath); | ||
| } | ||
|
|
||
| for (let i = 0; i < pending.length; i += CONCURRENCY) { | ||
| await Promise.all( | ||
| pending | ||
| .slice(i, i + CONCURRENCY) | ||
| .map((file) => compressFile(file, formats)), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Vite plugin that generates pre-compressed files for eligible build assets. | ||
| * @param {{ formats?: string[] }} [options] | ||
| * @returns {import('vite').Plugin} | ||
| */ | ||
| export default function compressPlugin(options = {}) { | ||
| const formats = options.formats ?? ["gzip"]; | ||
| validateFormats(formats, COMPRESSORS, "frontend compression format"); | ||
|
|
||
| return { | ||
| name: "vite-plugin-compress", | ||
| apply: "build", | ||
| enforce: "post", | ||
|
|
||
| async writeBundle(outputOptions) { | ||
| const outputDir = | ||
| outputOptions.dir ?? | ||
| (outputOptions.file ? dirname(outputOptions.file) : null); | ||
| if (!outputDir) return; | ||
| await compressDirectory(outputDir, formats); | ||
| }, | ||
| }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.