From 2e60037d9f8ef334bac93b864a8c7fec4a890b27 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 15 Feb 2019 16:11:33 +0100 Subject: [PATCH 1/7] add cache busting for app icons and msapp config xml --- src/vector/index.html | 24 ++++++++++++------------ webpack.config.js | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vector/index.html b/src/vector/index.html index 5451ec5232..f24007c964 100644 --- a/src/vector/index.html +++ b/src/vector/index.html @@ -3,22 +3,22 @@ Riot - - - - - - - - - + + + + + + + + + - + - - + + <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { diff --git a/webpack.config.js b/webpack.config.js index aba990541b..6786d9e492 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -61,7 +61,7 @@ module.exports = { }), }, { - test: /\.(gif|png|svg|ttf)$/, + test: /\.(gif|png|svg|ttf|xml|ico)$/, // Use a content-based hash in the name so that we can set a long cache // lifetime for assets while still delivering changes quickly. oneOf: [ From 878190ba279312ce906c20a3e9a1148fe06a399c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 18 Feb 2019 16:11:41 +0100 Subject: [PATCH 2/7] add content hash to individual language files --- package.json | 1 + scripts/copy-res.js | 104 ++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index de7847c34b..19cc5e6af1 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,7 @@ "karma-spec-reporter": "0.0.31", "karma-summary-reporter": "^1.5.1", "karma-webpack": "4.0.0-beta.0", + "loader-utils": "^1.2.3", "matrix-mock-request": "^1.2.2", "matrix-react-test-utils": "^0.2.0", "minimist": "^1.2.0", diff --git a/scripts/copy-res.js b/scripts/copy-res.js index cde96bc419..f1ed15b5d5 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -1,5 +1,7 @@ #!/usr/bin/env node +const loaderUtils = require("loader-utils"); + // copies the resources into the webapp directory. // @@ -61,12 +63,6 @@ const COPY_LIST = [ ["./config.json", "webapp", { directwatch: 1 }], ]; -INCLUDE_LANGS.forEach(function(l) { - COPY_LIST.push([ - l.value, "webapp/i18n/", { lang: 1 }, - ]); -}); - const parseArgs = require('minimist'); const Cpx = require('cpx'); const chokidar = require('chokidar'); @@ -77,8 +73,8 @@ const argv = parseArgs( process.argv.slice(2), {} ); -var watch = argv.w; -var verbose = argv.v; +const watch = argv.w; +const verbose = argv.v; function errCheck(err) { if (err) { @@ -136,39 +132,11 @@ function next(i, err) { .on('change', copy) .on('ready', cb) .on('error', errCheck); - } else if (opts.lang) { - const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + source + '.json'; - const riotWebFile = 'src/i18n/strings/' + source + '.json'; - - // XXX: Use a debounce because for some reason if we read the language - // file immediately after the FS event is received, the file contents - // appears empty. Possibly https://github.com/nodejs/node/issues/6112 - let makeLangDebouncer; - const makeLang = () => { - if (makeLangDebouncer) { - clearTimeout(makeLangDebouncer); - } - makeLangDebouncer = setTimeout(() => { - genLangFile(source, dest); - }, 500); - }; - - [reactSdkFile, riotWebFile].forEach(function(f) { - chokidar.watch(f) - .on('add', makeLang) - .on('change', makeLang) - //.on('ready', cb) We'd have to do this when both files are ready - .on('error', errCheck); - }); - next(i + 1, err); } else { cpx.on('watch-ready', cb); cpx.on("watch-error", cb); cpx.watch(); } - } else if (opts.lang) { - genLangFile(source, dest); - next(i + 1, err); } else { cpx.copy(cb); } @@ -195,21 +163,28 @@ function genLangFile(lang, dest) { translations = weblateToCounterpart(translations); - fs.writeFileSync(dest + lang + '.json', JSON.stringify(translations, null, 4)); + const json = JSON.stringify(translations, null, 4); + const jsonBuffer = Buffer.from(json); + const digest = loaderUtils.getHashDigest(jsonBuffer, "sha512", "base64", 7); + const filename = `${lang}.${digest}.json`; + + fs.writeFileSync(dest + filename, json); if (verbose) { - console.log("Generated language file: " + lang); + console.log("Generated language file: " + filename); } + + return filename; } -function genLangList() { +function genLangList(langFileMap) { const languages = {}; INCLUDE_LANGS.forEach(function(lang) { const normalizedLanguage = lang.value.toLowerCase().replace("_", "-"); const languageParts = normalizedLanguage.split('-'); if (languageParts.length == 2 && languageParts[0] == languageParts[1]) { - languages[languageParts[0]] = {'fileName': lang.value + '.json', 'label': lang.label}; + languages[languageParts[0]] = {'fileName': langFileMap[lang.value], 'label': lang.label}; } else { - languages[normalizedLanguage] = {'fileName': lang.value + '.json', 'label': lang.label}; + languages[normalizedLanguage] = {'fileName': langFileMap[lang.value], 'label': lang.label}; } }); fs.writeFile('webapp/i18n/languages.json', JSON.stringify(languages, null, 4), function(err) { @@ -257,5 +232,50 @@ function weblateToCounterpart(inTrs) { return outTrs; } -genLangList(); +/** +watch the input files for a given language, +regenerate the file, adding its content-hashed filename to langFileMap +and regenerating languages.json with the new filename +*/ +function watchLanguage(lang, dest, langFileMap) { + const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + lang + '.json'; + const riotWebFile = 'src/i18n/strings/' + lang + '.json'; + + // XXX: Use a debounce because for some reason if we read the language + // file immediately after the FS event is received, the file contents + // appears empty. Possibly https://github.com/nodejs/node/issues/6112 + let makeLangDebouncer; + const makeLang = () => { + if (makeLangDebouncer) { + clearTimeout(makeLangDebouncer); + } + makeLangDebouncer = setTimeout(() => { + const filename = genLangFile(lang, dest); + langFileMap[lang]=filename; + genLangList(langFileMap); + }, 500); + }; + + [reactSdkFile, riotWebFile].forEach(function(f) { + chokidar.watch(f) + .on('add', makeLang) + .on('change', makeLang) + .on('error', errCheck); + }); +} + +// language resources +const I18N_DEST = "webapp/i18n/"; +const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce((m, l) => { + const filename = genLangFile(l.value, I18N_DEST); + m[l.value] = filename; + return m; +}, {}); +genLangList(I18N_FILENAME_MAP); + +if (watch) { + INCLUDE_LANGS.forEach(l => watchLanguage(l.value, I18N_DEST, I18N_FILENAME_MAP)); +} + +// non-language resources next(0); From 070cc77e0fb1230e7d0335fbdbbe7a3bdf344a14 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 18 Feb 2019 16:12:04 +0100 Subject: [PATCH 3/7] add content hash to languages.json file using webpack file-loader --- webpack.config.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/webpack.config.js b/webpack.config.js index 6786d9e492..0aa43fddce 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -60,6 +60,20 @@ module.exports = { use: "css-loader", }), }, + { + // cache-bust languages.json file placed in + // riot-web/webapp/i18n during build by copy-res.js + test: /\.*languages.json$/, + type: "javascript/auto", + use: [ + { + loader: 'file-loader', + options: { + name: 'i18n/[name].[hash:7].[ext]', + }, + }, + ], + }, { test: /\.(gif|png|svg|ttf|xml|ico)$/, // Use a content-based hash in the name so that we can set a long cache From 223d8a87ca6725d21ac41432fcb76c74785ba894 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 18 Feb 2019 16:25:19 +0100 Subject: [PATCH 4/7] move relative path in react-sdk to webpack config file otherwise react-sdk can't build anymore with riot-web in a specific location --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 0aa43fddce..629e4db2a5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -162,8 +162,8 @@ module.exports = { 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV), }, + 'LANGUAGES_FILE': "'../../riot-web/webapp/i18n/languages.json'", // relative to languageHandler.js in matrix-react-sdk }), - new ExtractTextPlugin("bundles/[hash]/[name].css", { allChunks: true, }), From c2d14392b2e660619ebf3578fc417a4fbc5721a3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 18 Feb 2019 17:26:49 +0100 Subject: [PATCH 5/7] allow setting the relative location of the language file for running the unit tests in ci, where riot-web is a subdirectory of react-sdk --- webpack.config.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 629e4db2a5..c3d85caf78 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,6 +6,12 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); let og_image_url = process.env.RIOT_OG_IMAGE_URL; if (!og_image_url) og_image_url = 'https://riot.im/app/themes/riot/img/logos/riot-im-logo-black-text.png'; +// relative to languageHandler.js in matrix-react-sdk +let RIOT_LANGUAGES_FILE = process.env.RIOT_LANGUAGES_FILE; +if (!RIOT_LANGUAGES_FILE) { + RIOT_LANGUAGES_FILE = "../../riot-web/webapp/i18n/languages.json"; +} + module.exports = { entry: { // Load babel-polyfill first to avoid issues where some imports (namely react) @@ -162,7 +168,7 @@ module.exports = { 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV), }, - 'LANGUAGES_FILE': "'../../riot-web/webapp/i18n/languages.json'", // relative to languageHandler.js in matrix-react-sdk + 'LANGUAGES_FILE': JSON.stringify(RIOT_LANGUAGES_FILE), }), new ExtractTextPlugin("bundles/[hash]/[name].css", { allChunks: true, From e56206241ba1ae683e0eb3d14122210a8217b533 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 20 Feb 2019 09:39:27 +0100 Subject: [PATCH 6/7] use same hash format as webpack defaults --- scripts/copy-res.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/copy-res.js b/scripts/copy-res.js index f1ed15b5d5..4a2c6707d0 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -165,7 +165,7 @@ function genLangFile(lang, dest) { const json = JSON.stringify(translations, null, 4); const jsonBuffer = Buffer.from(json); - const digest = loaderUtils.getHashDigest(jsonBuffer, "sha512", "base64", 7); + const digest = loaderUtils.getHashDigest(jsonBuffer, null, null, 7); const filename = `${lang}.${digest}.json`; fs.writeFileSync(dest + filename, json); From 6a9c053536e7d4e1f95da7ca00f6b7be2f7388ac Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 20 Feb 2019 09:39:42 +0100 Subject: [PATCH 7/7] simplify config entry --- webpack.config.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index c3d85caf78..70ef0574a7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -71,14 +71,10 @@ module.exports = { // riot-web/webapp/i18n during build by copy-res.js test: /\.*languages.json$/, type: "javascript/auto", - use: [ - { - loader: 'file-loader', - options: { - name: 'i18n/[name].[hash:7].[ext]', - }, - }, - ], + loader: 'file-loader', + options: { + name: 'i18n/[name].[hash:7].[ext]', + }, }, { test: /\.(gif|png|svg|ttf|xml|ico)$/,