mirror of https://github.com/FreeTubeApp/FreeTube
Ensure updated locales are auto-reloaded in electron renderer dev mode (#4066)
* * Ensure updated locales are auto-reloaded in electron renderer dev mode * * Add watch external file plugin in dev mode only * * Make changes from code review
This commit is contained in:
parent
2c562d60ca
commit
342444f433
|
@ -1,4 +1,4 @@
|
||||||
const { existsSync, readFileSync } = require('fs')
|
const { existsSync, readFileSync, statSync } = require('fs')
|
||||||
const { brotliCompress, constants } = require('zlib')
|
const { brotliCompress, constants } = require('zlib')
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
const { load: loadYaml } = require('js-yaml')
|
const { load: loadYaml } = require('js-yaml')
|
||||||
|
@ -8,6 +8,7 @@ const brotliCompressAsync = promisify(brotliCompress)
|
||||||
class ProcessLocalesPlugin {
|
class ProcessLocalesPlugin {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
this.compress = !!options.compress
|
this.compress = !!options.compress
|
||||||
|
this.isIncrementalBuild = false
|
||||||
|
|
||||||
if (typeof options.inputDir !== 'string') {
|
if (typeof options.inputDir !== 'string') {
|
||||||
throw new Error('ProcessLocalesPlugin: no input directory `inputDir` specified.')
|
throw new Error('ProcessLocalesPlugin: no input directory `inputDir` specified.')
|
||||||
|
@ -21,10 +22,11 @@ class ProcessLocalesPlugin {
|
||||||
}
|
}
|
||||||
this.outputDir = options.outputDir
|
this.outputDir = options.outputDir
|
||||||
|
|
||||||
this.locales = []
|
this.locales = {}
|
||||||
this.localeNames = []
|
this.localeNames = []
|
||||||
|
this.activeLocales = []
|
||||||
|
|
||||||
this.cache = []
|
this.cache = {}
|
||||||
|
|
||||||
this.loadLocales()
|
this.loadLocales()
|
||||||
}
|
}
|
||||||
|
@ -37,18 +39,35 @@ class ProcessLocalesPlugin {
|
||||||
|
|
||||||
compilation.hooks.additionalAssets.tapPromise('process-locales-plugin', async (_assets) => {
|
compilation.hooks.additionalAssets.tapPromise('process-locales-plugin', async (_assets) => {
|
||||||
|
|
||||||
// While running in the webpack dev server, this hook gets called for every incrememental build.
|
// While running in the webpack dev server, this hook gets called for every incremental build.
|
||||||
// For incremental builds we can return the already processed versions, which saves time
|
// For incremental builds we can return the already processed versions, which saves time
|
||||||
// and makes webpack treat them as cached
|
// and makes webpack treat them as cached
|
||||||
if (IS_DEV_SERVER && this.cache.length > 0) {
|
|
||||||
for (const { filename, source } of this.cache) {
|
|
||||||
compilation.emitAsset(filename, source, { minimized: true })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const promises = []
|
const promises = []
|
||||||
|
// Prevents `loadLocales` called twice on first time (e.g. release build)
|
||||||
|
if (this.isIncrementalBuild) {
|
||||||
|
this.loadLocales(true)
|
||||||
|
} else {
|
||||||
|
this.isIncrementalBuild = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.values(this.locales).forEach((localeEntry) => {
|
||||||
|
const { locale, data, mtimeMs } = localeEntry
|
||||||
|
|
||||||
for (const { locale, data } of this.locales) {
|
|
||||||
promises.push(new Promise(async (resolve) => {
|
promises.push(new Promise(async (resolve) => {
|
||||||
|
if (IS_DEV_SERVER) {
|
||||||
|
const cacheEntry = this.cache[locale]
|
||||||
|
|
||||||
|
if (cacheEntry != null) {
|
||||||
|
const { filename, source, mtimeMs: cachedMtimeMs } = cacheEntry
|
||||||
|
|
||||||
|
if (cachedMtimeMs === mtimeMs) {
|
||||||
|
compilation.emitAsset(filename, source, { minimized: true })
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(data, 'Locale Name')) {
|
if (Object.prototype.hasOwnProperty.call(data, 'Locale Name')) {
|
||||||
delete data['Locale Name']
|
delete data['Locale Name']
|
||||||
}
|
}
|
||||||
|
@ -67,36 +86,51 @@ class ProcessLocalesPlugin {
|
||||||
|
|
||||||
if (IS_DEV_SERVER) {
|
if (IS_DEV_SERVER) {
|
||||||
source = new CachedSource(source)
|
source = new CachedSource(source)
|
||||||
this.cache.push({ filename, source })
|
this.cache[locale] = { filename, source, mtimeMs }
|
||||||
}
|
}
|
||||||
|
|
||||||
compilation.emitAsset(filename, source, { minimized: true })
|
compilation.emitAsset(filename, source, { minimized: true })
|
||||||
|
|
||||||
resolve()
|
resolve()
|
||||||
}))
|
}))
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
|
|
||||||
if (IS_DEV_SERVER) {
|
if (IS_DEV_SERVER) {
|
||||||
// we don't need the unmodified sources anymore, as we use the cache `this.cache`
|
// we don't need the unmodified sources anymore, as we use the cache `this.cache`
|
||||||
// so we can clear this to free some memory
|
// so we can clear this to free some memory
|
||||||
delete this.locales
|
delete localeEntry.data
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadLocales() {
|
loadLocales(loadModifiedFilesOnly = false) {
|
||||||
const activeLocales = JSON.parse(readFileSync(`${this.inputDir}/activeLocales.json`))
|
if (this.activeLocales.length === 0) {
|
||||||
|
this.activeLocales = JSON.parse(readFileSync(`${this.inputDir}/activeLocales.json`))
|
||||||
|
}
|
||||||
|
|
||||||
for (const locale of activeLocales) {
|
for (const locale of this.activeLocales) {
|
||||||
const contents = readFileSync(`${this.inputDir}/${locale}.yaml`, 'utf-8')
|
const filePath = `${this.inputDir}/${locale}.yaml`
|
||||||
|
// Cannot use `mtime` since values never equal
|
||||||
|
const mtimeMsFromStats = statSync(filePath).mtimeMs
|
||||||
|
if (loadModifiedFilesOnly) {
|
||||||
|
// Skip reading files where mtime (modified time) same as last read
|
||||||
|
// (stored in mtime)
|
||||||
|
const existingMtime = this.locales[locale]?.mtimeMs
|
||||||
|
if (existingMtime != null && existingMtime === mtimeMsFromStats) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const contents = readFileSync(filePath, 'utf-8')
|
||||||
const data = loadYaml(contents)
|
const data = loadYaml(contents)
|
||||||
|
this.locales[locale] = { locale, data, mtimeMs: mtimeMsFromStats }
|
||||||
|
|
||||||
this.localeNames.push(data['Locale Name'] ?? locale)
|
const localeName = data['Locale Name'] ?? locale
|
||||||
this.locales.push({ locale, data })
|
if (!loadModifiedFilesOnly) {
|
||||||
|
this.localeNames.push(localeName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const { readFileSync } = require('fs')
|
||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
||||||
const ProcessLocalesPlugin = require('./ProcessLocalesPlugin')
|
const ProcessLocalesPlugin = require('./ProcessLocalesPlugin')
|
||||||
|
const WatchExternalFilesPlugin = require('webpack-watch-external-files-plugin')
|
||||||
|
|
||||||
const isDevMode = process.env.NODE_ENV === 'development'
|
const isDevMode = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
|
@ -128,7 +130,7 @@ const config = {
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: isDevMode ? '[name].css' : '[name].[contenthash].css',
|
filename: isDevMode ? '[name].css' : '[name].[contenthash].css',
|
||||||
chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css',
|
chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css',
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
@ -146,4 +148,16 @@ const config = {
|
||||||
target: 'electron-renderer',
|
target: 'electron-renderer',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDevMode) {
|
||||||
|
const activeLocales = JSON.parse(readFileSync(path.join(__dirname, '../static/locales/activeLocales.json')))
|
||||||
|
|
||||||
|
config.plugins.push(
|
||||||
|
new WatchExternalFilesPlugin({
|
||||||
|
files: [
|
||||||
|
`./static/locales/{${activeLocales.join(',')}}.yaml`,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = config
|
module.exports = config
|
||||||
|
|
|
@ -127,6 +127,7 @@
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
|
"webpack-watch-external-files-plugin": "^2.0.0",
|
||||||
"yaml-eslint-parser": "^1.2.2"
|
"yaml-eslint-parser": "^1.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -4491,6 +4491,17 @@ glob-to-regexp@^0.4.1:
|
||||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||||
|
|
||||||
|
glob@8.1.0:
|
||||||
|
version "8.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
|
||||||
|
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^5.0.1"
|
||||||
|
once "^1.3.0"
|
||||||
|
|
||||||
glob@^10.3.7:
|
glob@^10.3.7:
|
||||||
version "10.3.10"
|
version "10.3.10"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
|
||||||
|
@ -6476,6 +6487,14 @@ path-type@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
|
path@0.12.7:
|
||||||
|
version "0.12.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
|
||||||
|
integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==
|
||||||
|
dependencies:
|
||||||
|
process "^0.11.1"
|
||||||
|
util "^0.10.3"
|
||||||
|
|
||||||
pend@~1.2.0:
|
pend@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||||
|
@ -6858,7 +6877,7 @@ process-nextick-args@~2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
process@^0.11.10:
|
process@^0.11.1, process@^0.11.10:
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||||
|
@ -8431,6 +8450,13 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
|
|
||||||
|
util@^0.10.3:
|
||||||
|
version "0.10.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||||
|
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
|
||||||
|
dependencies:
|
||||||
|
inherits "2.0.3"
|
||||||
|
|
||||||
util@^0.12.4:
|
util@^0.12.4:
|
||||||
version "0.12.4"
|
version "0.12.4"
|
||||||
resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
|
resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
|
||||||
|
@ -8750,6 +8776,14 @@ webpack-sources@^3.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||||
|
|
||||||
|
webpack-watch-external-files-plugin@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/webpack-watch-external-files-plugin/-/webpack-watch-external-files-plugin-2.0.0.tgz#4c4e08c59b092c1d705e94d7a380eaa827a76d9c"
|
||||||
|
integrity sha512-dPUCEcgBjJHWyD4b0HcHD6h3mkPxVKmsfUSks5CvCbrF7HHLvIl/Wq4KSkUMMiNymloZKKkBjYpQxhubAEFM+Q==
|
||||||
|
dependencies:
|
||||||
|
glob "8.1.0"
|
||||||
|
path "0.12.7"
|
||||||
|
|
||||||
webpack@^5.89.0:
|
webpack@^5.89.0:
|
||||||
version "5.89.0"
|
version "5.89.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc"
|
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc"
|
||||||
|
|
Loading…
Reference in New Issue