From 105cf03e700e6a373a25ad008c4a93165fb98882 Mon Sep 17 00:00:00 2001 From: Tom Ross Date: Fri, 15 Oct 2021 11:55:12 +0100 Subject: [PATCH] Performance: Pre-compress and serve Brotli assets (#25952) --- .gitignore | 4 +++ client/web/dev/server/production.server.ts | 15 ++++++--- client/web/webpack.config.js | 26 +++++++++++++++ go.mod | 2 +- go.sum | 2 ++ package.json | 3 ++ yarn.lock | 39 +++++++++++++++++----- 7 files changed, 77 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index ff0f2bc9caa..5abf7bad35e 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,10 @@ yarn-error.log /ui/assets/extension /ui/.tmp /ui/assets/scripts/ +/ui/assets/styles/ +/ui/assets/*.br +/ui/assets/*.gz + *.json.actual eb-bundle.zip diff --git a/client/web/dev/server/production.server.ts b/client/web/dev/server/production.server.ts index 9f6eb8c24aa..fd9f2df9422 100644 --- a/client/web/dev/server/production.server.ts +++ b/client/web/dev/server/production.server.ts @@ -1,7 +1,7 @@ import chalk from 'chalk' -import compression from 'compression' import historyApiFallback from 'connect-history-api-fallback' import express, { RequestHandler } from 'express' +import expressStaticGzip from 'express-static-gzip' import { createProxyMiddleware } from 'http-proxy-middleware' import signale from 'signale' @@ -15,7 +15,6 @@ import { STATIC_INDEX_PATH, HTTP_WEB_SERVER_URL, HTTPS_WEB_SERVER_URL, - shouldCompressResponse, } from '../utils' const { SOURCEGRAPH_API_URL, CLIENT_PROXY_DEVELOPMENT_PORT } = environmentConfig @@ -31,15 +30,21 @@ async function startProductionServer(): Promise { const app = express() - // Compress all HTTP responses - app.use(compression({ filter: shouldCompressResponse })) // Serve index.html in place of any 404 responses. app.use(historyApiFallback() as RequestHandler) // Attach `CSRF_COOKIE_NAME` cookie to every response to avoid "CSRF token is invalid" API error. app.use(getCSRFTokenCookieMiddleware(csrfCookieValue)) // Serve build artifacts. - app.use('/.assets', express.static(STATIC_ASSETS_PATH)) + + app.use( + '/.assets', + expressStaticGzip(STATIC_ASSETS_PATH, { + enableBrotli: true, + orderPreference: ['br', 'gz'], + index: false, + }) + ) // Proxy API requests to the `process.env.SOURCEGRAPH_API_URL`. app.use( diff --git a/client/web/webpack.config.js b/client/web/webpack.config.js index 617263e3d86..f7bf7c5b8f8 100644 --- a/client/web/webpack.config.js +++ b/client/web/webpack.config.js @@ -3,6 +3,7 @@ const path = require('path') const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin') +const CompressionPlugin = require('compression-webpack-plugin') const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin') const logger = require('gulplog') const MiniCssExtractPlugin = require('mini-css-extract-plugin') @@ -157,6 +158,31 @@ const config = { shouldAnalyze && new BundleAnalyzerPlugin(), isHotReloadEnabled && new webpack.HotModuleReplacementPlugin(), isHotReloadEnabled && new ReactRefreshWebpackPlugin({ overlay: false }), + isProduction && + new CompressionPlugin({ + filename: '[path][base].gz', + algorithm: 'gzip', + test: /\.(js|css|svg)$/, + compressionOptions: { + /** Maximum compression level for Gzip */ + level: 9, + }, + }), + isProduction && + new CompressionPlugin({ + filename: '[path][base].br', + algorithm: 'brotliCompress', + test: /\.(js|css|svg)$/, + compressionOptions: { + /** Maximum compression level for Brotli */ + level: 11, + }, + /** + * We get little/no benefits from compressing files that are already under this size. + * We can fall back to dynamic gzip for these. + */ + threshold: 10240, + }), ].filter(Boolean), resolve: { extensions: ['.mjs', '.ts', '.tsx', '.js', '.json'], diff --git a/go.mod b/go.mod index 4dd28f8405a..6128ea9224f 100644 --- a/go.mod +++ b/go.mod @@ -341,7 +341,7 @@ replace ( // Pending: https://github.com/ghodss/yaml/pull/65 github.com/ghodss/yaml => github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152 // Pending: https://github.com/shurcooL/httpgzip/pull/9 - github.com/shurcooL/httpgzip => github.com/sourcegraph/httpgzip v0.0.0-20210213125624-48ebf036a6a1 + github.com/shurcooL/httpgzip => github.com/sourcegraph/httpgzip v0.0.0-20211015085752-0bad89b3b4df ) // Status unclear replace directives diff --git a/go.sum b/go.sum index 0999d66f4a4..5e85b349367 100644 --- a/go.sum +++ b/go.sum @@ -1410,6 +1410,8 @@ github.com/sourcegraph/gosyntect v0.0.0-20210422223331-645353f16ddc h1:iaDqNUpXf github.com/sourcegraph/gosyntect v0.0.0-20210422223331-645353f16ddc/go.mod h1:WiNJKgKTnR3psOIGzVZQjLqZjJZuoL3F8tCh25Uk8dU= github.com/sourcegraph/httpgzip v0.0.0-20210213125624-48ebf036a6a1 h1:o9U79WCNQjxxK2RYhIvflq7PK6JBbG70O2qG2haR26M= github.com/sourcegraph/httpgzip v0.0.0-20210213125624-48ebf036a6a1/go.mod h1:RqWagzxNGCvucQQC9vX6aps474LCCOgshDpUTTyb+O8= +github.com/sourcegraph/httpgzip v0.0.0-20211015085752-0bad89b3b4df h1:VaS8k40GiNVUxVx0ZUilU38NU6tWUHNQOX342DWtZUM= +github.com/sourcegraph/httpgzip v0.0.0-20211015085752-0bad89b3b4df/go.mod h1:RqWagzxNGCvucQQC9vX6aps474LCCOgshDpUTTyb+O8= github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf h1:oAdWFqhStsWiiMP/vkkHiMXqFXzl1XfUNOdxKJbd6bI= github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf/go.mod h1:ppFaPm6kpcHnZGqQTFhUIAQRIEhdQDWP1PCv4/ON354= github.com/sourcegraph/oauth2 v0.0.0-20210825125341-77c1d99ece3c h1:HGa4iJr6MGKnB5qbU7tI511NdGuHUHnNCqP67G6KmfE= diff --git a/package.json b/package.json index 83459da8a33..bd2695a0b76 100644 --- a/package.json +++ b/package.json @@ -156,6 +156,7 @@ "@types/classnames": "2.2.10", "@types/command-exists": "1.2.0", "@types/compression": "^1.7.2", + "@types/compression-webpack-plugin": "^9.0.0", "@types/connect-history-api-fallback": "^1.3.4", "@types/css-minimizer-webpack-plugin": "^3.0.1", "@types/d3-axis": "1.0.12", @@ -231,6 +232,7 @@ "chrome-webstore-upload-cli": "^1.2.0", "command-exists": "^1.2.9", "compression": "^1.7.4", + "compression-webpack-plugin": "^9.0.0", "connect-history-api-fallback": "^1.6.0", "cross-env": "^7.0.2", "css-loader": "^5.2.6", @@ -244,6 +246,7 @@ "eslint-plugin-monorepo": "^0.3.2", "execa": "^5.0.0", "express": "^4.17.1", + "express-static-gzip": "^2.1.1", "fancy-log": "^1.3.3", "get-port": "^5.1.1", "glob": "^7.1.6", diff --git a/yarn.lock b/yarn.lock index 12981700619..a144f68a879 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4799,6 +4799,14 @@ resolved "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.0.tgz#d97e0ed10097090e4ab0367ed425b0312fad86f3" integrity sha512-ugsxEJfsCuqMLSuCD4PIJkp5Uk2z6TCMRCgYVuhRo5cYQY3+1xXTQkSlPtkpGHuvWMjS2KTeVQXxkXRACMbM6A== +"@types/compression-webpack-plugin@^9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@types/compression-webpack-plugin/-/compression-webpack-plugin-9.0.0.tgz#d7d504e2268e84e1413a99c072d6ff9aee31f213" + integrity sha512-3DdireWRs+SoAIKhbBIowoUMwMOXVKrDHeIO82e7D6/yJRw6kgzFhCnamJJNo10uHJ7YqP1h+g5itW+HlLw7Lg== + dependencies: + tapable "^2.2.0" + webpack "^5.51.0" + "@types/compression@^1.7.2": version "1.7.2" resolved "https://registry.npmjs.org/@types/compression/-/compression-1.7.2.tgz#7cc1cdb01b4730eea284615a68fc70a2cdfd5e71" @@ -8926,6 +8934,14 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.43.0 < 2" +compression-webpack-plugin@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-9.0.0.tgz#38b4e18313f5d704c453ab5645e38a5805ba5889" + integrity sha512-V2KmQqaUkErPT+ZcUGHa8zWpIw1oTYaC7wjGewJm053GWAoY04GfU5B/NZ/JSz1eFp9MggMdLQpEHe1TJAQY1A== + dependencies: + schema-utils "^3.1.0" + serialize-javascript "^6.0.0" + compression@^1.7.4: version "1.7.4" resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" @@ -11551,6 +11567,13 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" +express-static-gzip@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.1.tgz#ee3a0eca3f10348bcd214095fbb46f8d0f8cb471" + integrity sha512-J+xSzdr5lj1cIuZey0ac6nUv22VE7GrdwTERqE8DsrqSXLm1zjeYWTVbK37t8exGwobxBXeWU2bM7eSMjBR4YA== + dependencies: + serve-static "^1.14.1" + express@^4.17.1: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -13375,9 +13398,9 @@ hex-color-regex@^1.1.0: integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== highlight.js@^10.0.0, highlight.js@^10.1.1, highlight.js@^10.5.0: - version "10.7.2" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" - integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== + version "10.7.3" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== highlight.js@~10.4.0: version "10.4.1" @@ -21085,7 +21108,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.14.1: +serve-static@1.14.1, serve-static@^1.14.1: version "1.14.1" resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== @@ -23106,9 +23129,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== uglify-js@^3.1.4: - version "3.13.5" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113" - integrity sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw== + version "3.14.2" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99" + integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== ultron@~1.1.0: version "1.1.1" @@ -24145,7 +24168,7 @@ webpack-virtual-modules@^0.4.1: resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz#cd597c6d51d5a5ecb473eea1983a58fa8a17ded9" integrity sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw== -webpack@4, webpack@5, webpack@^5, webpack@^5.1.0, webpack@^5.37.0, webpack@^5.38.1, webpack@^5.45.1, webpack@^5.9.0: +webpack@4, webpack@5, webpack@^5, webpack@^5.1.0, webpack@^5.37.0, webpack@^5.38.1, webpack@^5.45.1, webpack@^5.51.0, webpack@^5.9.0: version "5.45.1" resolved "https://registry.npmjs.org/webpack/-/webpack-5.45.1.tgz#d78dcbeda18a872dc62b0455d3ed3dcfd1c886bb" integrity sha512-68VT2ZgG9EHs6h6UxfV2SEYewA9BA3SOLSnC2NEbJJiEwbAiueDL033R1xX0jzjmXvMh0oSeKnKgbO2bDXIEyQ==