diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2021-10-05 17:00:10 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-10-06 20:03:08 +0000 |
commit | fea5cec5eb9b26cbf42f98f6ef79a8e01028af0e (patch) | |
tree | 9e1795ed93d3e64034859daa0204ca39f91a2c52 /server | |
parent | 3901540ab1ae1bf0f5c63b4ed7abbf3c75ef290b (diff) | |
download | sonarqube-fea5cec5eb9b26cbf42f98f6ef79a8e01028af0e.tar.gz sonarqube-fea5cec5eb9b26cbf42f98f6ef79a8e01028af0e.zip |
[NO-JIRA] Fix frontend caching issue
Diffstat (limited to 'server')
-rw-r--r-- | server/sonar-web/config/esbuild-config.js | 54 | ||||
-rw-r--r-- | server/sonar-web/config/esbuild-html-plugin.js | 60 | ||||
-rw-r--r-- | server/sonar-web/config/indexHtmlTemplate.js (renamed from server/sonar-web/public/index.html) | 25 | ||||
-rw-r--r-- | server/sonar-web/scripts/build.js | 2 | ||||
-rw-r--r-- | server/sonar-web/scripts/start.js | 39 |
5 files changed, 154 insertions, 26 deletions
diff --git a/server/sonar-web/config/esbuild-config.js b/server/sonar-web/config/esbuild-config.js index c1cec016ba5..8497d8978da 100644 --- a/server/sonar-web/config/esbuild-config.js +++ b/server/sonar-web/config/esbuild-config.js @@ -22,26 +22,12 @@ const postCssPlugin = require('esbuild-plugin-postcss2').default; const postCssCalc = require('postcss-calc'); const postCssCustomProperties = require('postcss-custom-properties'); const documentationPlugin = require('./esbuild-documentation-plugin'); +const htmlPlugin = require('./esbuild-html-plugin'); +const htmlTemplate = require('./indexHtmlTemplate'); const { getCustomProperties } = require('./utils'); -module.exports = release => ({ - entryPoints: ['src/main/js/app/index.ts'], - tsconfig: './tsconfig.json', - external: ['/images/*'], - loader: { - '.png': 'dataurl', - '.md': 'text' - }, - define: { - 'process.cwd': 'dummy_process_cwd' - }, - inject: ['config/process-shim.js'], - bundle: true, - minify: release, - sourcemap: true, - target: ['chrome58', 'firefox57', 'safari11', 'edge18'], - outfile: 'build/webapp/js/out.js', - plugins: [ +module.exports = release => { + const plugins = [ postCssPlugin({ plugins: [ autoprefixer, @@ -53,5 +39,33 @@ module.exports = release => ({ ] }), documentationPlugin() - ] -}); + ]; + + if (release) { + // Only create index.html from template when releasing + // The devserver will generate its own index file from the template + plugins.push(htmlPlugin()); + } + + return { + entryPoints: ['src/main/js/app/index.ts'], + tsconfig: './tsconfig.json', + external: ['/images/*'], + loader: { + '.png': 'dataurl', + '.md': 'text' + }, + define: { + 'process.cwd': 'dummy_process_cwd' + }, + inject: ['config/process-shim.js'], + bundle: true, + minify: release, + metafile: true, + sourcemap: true, + target: ['chrome58', 'firefox57', 'safari11', 'edge18'], + outdir: 'build/webapp/js', + entryNames: release ? 'out[hash]' : 'out', + plugins + }; +}; diff --git a/server/sonar-web/config/esbuild-html-plugin.js b/server/sonar-web/config/esbuild-html-plugin.js new file mode 100644 index 00000000000..7ab0a9ff5f0 --- /dev/null +++ b/server/sonar-web/config/esbuild-html-plugin.js @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +const fs = require('fs-extra'); +const path = require('path'); +const paths = require('./paths'); +const htmlTemplate = require('./indexHtmlTemplate'); + +function extractHash(filename) { + const regexp = /out([\w]+)\./; + const result = filename.match(regexp); + if (!result) { + throw Error('filename format error: could not extract hash'); + } + + return result[1]; +} + +/* + * This plugin generates a index.html file from the template, + * injecting the right hash values to the imported js and css files + */ +module.exports = () => ({ + name: 'html-plugin', + setup({ onEnd }) { + onEnd(result => { + const files = result.metafile.outputs; + + let cssHash; + let jsHash; + for (const filename in files) { + if (filename.endsWith('css')) { + cssHash = extractHash(filename); + } else if (filename.endsWith('js')) { + jsHash = extractHash(filename); + } + } + + const htmlContents = htmlTemplate(cssHash, jsHash); + + fs.writeFile(path.join(paths.appBuild, 'index.html'), htmlContents); + }); + } +}); diff --git a/server/sonar-web/public/index.html b/server/sonar-web/config/indexHtmlTemplate.js index 7c6fb6a4194..0a0695cfc79 100644 --- a/server/sonar-web/public/index.html +++ b/server/sonar-web/config/indexHtmlTemplate.js @@ -1,3 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +module.exports = (cssHash, jsHash) => ` <!DOCTYPE html> <html lang="en"> @@ -20,7 +40,7 @@ <meta name="msapplication-TileImage" content="%WEB_CONTEXT%/mstile-512x512.png" /> <title>%INSTANCE%</title> - <link rel="stylesheet" href="%WEB_CONTEXT%/js/out.css"> + <link rel="stylesheet" href="%WEB_CONTEXT%/js/out${cssHash}.css"> </head> <body> @@ -38,7 +58,8 @@ window.official = %OFFICIAL%; </script> - <script type="module" src="%WEB_CONTEXT%/js/out.js"></script> + <script type="module" src="%WEB_CONTEXT%/js/out${jsHash}.js"></script> </body> </html> +`; diff --git a/server/sonar-web/scripts/build.js b/server/sonar-web/scripts/build.js index 42d461449f9..d81c59adb0f 100644 --- a/server/sonar-web/scripts/build.js +++ b/server/sonar-web/scripts/build.js @@ -39,7 +39,7 @@ async function build() { console.log(chalk.cyan.bold(`Creating ${release ? 'optimized' : 'fast'} production build...`)); console.log(); - await esbuild.build(getConfig(release)); + await esbuild.build(getConfig(release)).catch(() => process.exit(1)); console.log(chalk.green.bold('Compiled successfully!')); console.log(chalk.cyan(Math.round(performance.now() - start), 'ms')); diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js index ceb9cc1cdb4..64fc06ca851 100644 --- a/server/sonar-web/scripts/start.js +++ b/server/sonar-web/scripts/start.js @@ -27,6 +27,10 @@ const http = require('http'); const httpProxy = require('http-proxy'); const getConfig = require('../config/esbuild-config'); const { getMessages } = require('./utils'); +const paths = require('../config/paths'); + +const STATUS_OK = 200; +const STATUS_ERROR = 500; const port = process.env.PORT || 3000; const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; @@ -38,16 +42,39 @@ const config = getConfig(false); function handleL10n(res) { getMessages() .then(messages => { - res.writeHead(200, { 'Content-Type': 'application/json' }); + res.writeHead(STATUS_OK, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ effectiveLocale: 'en', messages })); }) .catch(e => { console.error(e); - res.writeHead(500); + res.writeHead(STATUS_ERROR); res.end(e); }); } +function handleStaticFileRequest(req, res) { + fs.readFile(paths.appBuild + req.url, (err, data) => { + if (err) { + // Any unknown path should go to the index.html + const htmlTemplate = require('../config/indexHtmlTemplate'); + + // Replace hash placeholders as well as all the + // tags that are usually replaced by the server + const content = htmlTemplate('', '') + .replace(/%WEB_CONTEXT%/g, '') + .replace(/%SERVER_STATUS%/g, 'UP') + .replace(/%INSTANCE%/g, 'SonarQube') + .replace(/%OFFICIAL%/g, 'true'); + + res.writeHead(STATUS_OK); + res.end(content); + } else { + res.writeHead(STATUS_OK); + res.end(data); + } + }); +} + function run() { console.log('starting...'); esbuild @@ -83,7 +110,11 @@ function run() { esbuildProxy.web(req, res); } else if (req.url.match(/l10n\/index/)) { handleL10n(res); - } else { + } else if ( + req.url.includes('api/') || + req.url.includes('images/') || + req.url.includes('static/') + ) { proxy.web( req, res, @@ -92,6 +123,8 @@ function run() { }, e => console.error('req error', e) ); + } else { + handleStaticFileRequest(req, res); } }) .listen(port); |