Merge pull request #8710 from vector-im/bwindels/moarcachebustin
Cache busting for icons & language filespull/8854/head
						commit
						ad04e8bee8
					
				|  | @ -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", | ||||
|  |  | |||
|  | @ -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, null, null, 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); | ||||
|  |  | |||
|  | @ -3,22 +3,22 @@ | |||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>Riot</title> | ||||
|     <link rel="apple-touch-icon" sizes="57x57" href="vector-icons/apple-touch-icon-57x57.png"> | ||||
|     <link rel="apple-touch-icon" sizes="60x60" href="vector-icons/apple-touch-icon-60x60.png"> | ||||
|     <link rel="apple-touch-icon" sizes="72x72" href="vector-icons/apple-touch-icon-72x72.png"> | ||||
|     <link rel="apple-touch-icon" sizes="76x76" href="vector-icons/apple-touch-icon-76x76.png"> | ||||
|     <link rel="apple-touch-icon" sizes="114x114" href="vector-icons/apple-touch-icon-114x114.png"> | ||||
|     <link rel="apple-touch-icon" sizes="120x120" href="vector-icons/apple-touch-icon-120x120.png"> | ||||
|     <link rel="apple-touch-icon" sizes="144x144" href="vector-icons/apple-touch-icon-144x144.png"> | ||||
|     <link rel="apple-touch-icon" sizes="152x152" href="vector-icons/apple-touch-icon-152x152.png"> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="vector-icons/apple-touch-icon-180x180.png"> | ||||
|     <link rel="apple-touch-icon" sizes="57x57" href="<%= require('../../res/vector-icons/apple-touch-icon-57x57.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="60x60" href="<%= require('../../res/vector-icons/apple-touch-icon-60x60.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="72x72" href="<%= require('../../res/vector-icons/apple-touch-icon-72x72.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="76x76" href="<%= require('../../res/vector-icons/apple-touch-icon-76x76.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="114x114" href="<%= require('../../res/vector-icons/apple-touch-icon-114x114.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="120x120" href="<%= require('../../res/vector-icons/apple-touch-icon-120x120.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="144x144" href="<%= require('../../res/vector-icons/apple-touch-icon-144x144.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="152x152" href="<%= require('../../res/vector-icons/apple-touch-icon-152x152.png') %>"> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="<%= require('../../res/vector-icons/apple-touch-icon-180x180.png') %>"> | ||||
|     <link rel="manifest" href="manifest.json"> | ||||
|     <link rel="shortcut icon" href="vector-icons/favicon.ico"> | ||||
|     <link rel="shortcut icon" href="<%= require('../../res/vector-icons/favicon.ico') %>"> | ||||
|     <meta name="apple-mobile-web-app-title" content="Riot"> | ||||
|     <meta name="application-name" content="Riot"> | ||||
|     <meta name="msapplication-TileColor" content="#da532c"> | ||||
|     <meta name="msapplication-TileImage" content="vector-icons/mstile-144x144.png"> | ||||
|     <meta name="msapplication-config" content="vector-icons/browserconfig.xml"> | ||||
|     <meta name="msapplication-TileImage" content="<%= require('../../res/vector-icons/mstile-144x144.png') %>"> | ||||
|     <meta name="msapplication-config" content="<%= require('../../res/vector-icons/browserconfig.xml') %>"> | ||||
|     <meta name="theme-color" content="#ffffff"> | ||||
|     <meta property="og:image" content="<%= htmlWebpackPlugin.options.vars.og_image_url %>" /> | ||||
|     <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { | ||||
|  |  | |||
|  | @ -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)
 | ||||
|  | @ -61,7 +67,17 @@ module.exports = { | |||
|                 }), | ||||
|             }, | ||||
|             { | ||||
|                 test: /\.(gif|png|svg|ttf)$/, | ||||
|                 // cache-bust languages.json file placed in
 | ||||
|                 // riot-web/webapp/i18n during build by copy-res.js
 | ||||
|                 test: /\.*languages.json$/, | ||||
|                 type: "javascript/auto", | ||||
|                 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
 | ||||
|                 // lifetime for assets while still delivering changes quickly.
 | ||||
|                 oneOf: [ | ||||
|  | @ -148,8 +164,8 @@ module.exports = { | |||
|             'process.env': { | ||||
|                 NODE_ENV: JSON.stringify(process.env.NODE_ENV), | ||||
|             }, | ||||
|             'LANGUAGES_FILE': JSON.stringify(RIOT_LANGUAGES_FILE), | ||||
|         }), | ||||
| 
 | ||||
|         new ExtractTextPlugin("bundles/[hash]/[name].css", { | ||||
|             allChunks: true, | ||||
|         }), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Bruno Windels
						Bruno Windels