Merge pull request #8710 from vector-im/bwindels/moarcachebustin
Cache busting for icons & language files
This commit is contained in:
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