From 574deabdd45d129ec76858cd443e9da16b46401d Mon Sep 17 00:00:00 2001 From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com> Date: Tue, 19 Jul 2022 20:20:50 +0300 Subject: [PATCH] feat: add the ability to pass a getter for i18n instance --- client.d.ts | 2 +- lib/client-hmr.js | 42 +++++++++++++----------- lib/server-hmr.js | 27 +++++++-------- lib/utils.js | 84 ++++++++++++++++++++++++++++++++--------------- server.d.ts | 2 +- 5 files changed, 97 insertions(+), 60 deletions(-) diff --git a/client.d.ts b/client.d.ts index 0f54c7f..e3f8953 100644 --- a/client.d.ts +++ b/client.d.ts @@ -1,7 +1,7 @@ import i18next from 'i18next'; declare const client: { - applyClientHMR(i18n: i18next.i18n): void; + applyClientHMR(i18nOrGetter: i18next.i18n | (() => i18next.i18n)): void; }; export = client; diff --git a/lib/client-hmr.js b/lib/client-hmr.js index 114689c..9ff0f68 100644 --- a/lib/client-hmr.js +++ b/lib/client-hmr.js @@ -1,11 +1,13 @@ -const { extractLangAndNS, printList } = require('./utils'); +const { extractList, printList, makeUniqueList, createLoggerOnce } = require('./utils'); -module.exports = function applyClientHMR(i18n) { +module.exports = function applyClientHMR(i18nOrGetter) { if (module.hot) { function log(msg, type = 'log') { console[type](`[%cI18NextHMR%c] ${msg}`, 'color:#bc93b6', ''); } + const logOnce = createLoggerOnce(log); + const { changedFile } = require('./trigger.js'); if (!changedFile || changedFile) { @@ -13,29 +15,31 @@ module.exports = function applyClientHMR(i18n) { log('Client HMR has started'); } - let backendOptions = { queryStringParams: {} }; - try { - backendOptions = i18n.options.backend || i18n.services.backendConnector.backend.options; - backendOptions.queryStringParams = backendOptions.queryStringParams || {}; - } catch (e) { - log('Client i18next-backend not found, hmr may not work', 'warn'); - } + async function reloadTranslations(list, i18nInstance) { + let backendOptions = { queryStringParams: {} }; + try { + backendOptions = + i18nInstance.options.backend || i18nInstance.services.backendConnector.backend.options; + backendOptions.queryStringParams = backendOptions.queryStringParams || {}; + } catch (e) { + logOnce('Client i18next-http-backend not found, hmr may not work', 'warn'); + } - async function reloadTranslations(list) { backendOptions.queryStringParams._ = new Date().getTime(); // cache killer - const langs = [...new Set(list.map((item) => item.lang))]; - const namespaces = [...new Set(list.map((item) => item.ns))]; - await i18n.reloadResources(langs, namespaces, (error) => { + const langs = makeUniqueList(list.map((item) => item.lang)); + const namespaces = makeUniqueList(list.map((item) => item.ns)); + + await i18nInstance.reloadResources(langs, namespaces, (error) => { if (error) { log(error, 'error'); return; } - const currentLang = i18n.language; + const currentLang = i18nInstance.language; if (langs.includes(currentLang)) { - i18n.changeLanguage(currentLang); + i18nInstance.changeLanguage(currentLang); log(`Update applied successfully`); } else { log(`Resources of '${printList(list)}' were reloaded successfully`); @@ -45,9 +49,9 @@ module.exports = function applyClientHMR(i18n) { module.hot.accept('./trigger.js', () => { const { changedFiles } = require('./trigger.js'); - const list = changedFiles - .map((changedFile) => extractLangAndNS(changedFile, i18n.options.ns)) - .filter(({ lang, ns }) => Boolean(lang) && Boolean(ns)); + const i18nInstance = typeof i18nOrGetter === 'function' ? i18nOrGetter() : i18nOrGetter; + + const list = extractList(changedFiles, i18nInstance.options.ns); if (!list.length) { return; @@ -55,7 +59,7 @@ module.exports = function applyClientHMR(i18n) { log(`Got an update with ${printList(list)}`); - return reloadTranslations(list); + return reloadTranslations(list, i18nInstance); }); } }; diff --git a/lib/server-hmr.js b/lib/server-hmr.js index 07b3c17..3d8cb60 100644 --- a/lib/server-hmr.js +++ b/lib/server-hmr.js @@ -1,16 +1,17 @@ -const { extractLangAndNS, printList } = require('./utils'); +const { extractList, printList, makeUniqueList, createLoggerOnce } = require('./utils'); -module.exports = function applyServerHMR(i18n) { +module.exports = function applyServerHMR(i18nOrGetter) { const pluginName = `\x1b[35m\x1b[1m${'I18NextHMR'}\x1b[0m\x1b[39m`; - - function log(message) { - console.log(`[ ${pluginName} ] ${message}`); + function log(message, type = 'log') { + console[type](`[ ${pluginName} ] ${message}`); } + const logOnce = createLoggerOnce(log); + function reloadServerTranslation({ changedFiles }) { - const list = changedFiles - .map((changedFile) => extractLangAndNS(changedFile, i18n.options.ns)) - .filter(({ lang, ns }) => Boolean(lang) && Boolean(ns)); + const i18nInstance = typeof i18nOrGetter === 'function' ? i18nOrGetter() : i18nOrGetter; + + const list = extractList(changedFiles, i18nInstance.options.ns); if (list.length === 0) { return; @@ -18,10 +19,10 @@ module.exports = function applyServerHMR(i18n) { log(`Got an update with ${printList(list)}`); - const langs = [...new Set(list.map((item) => item.lang))]; - const namespaces = [...new Set(list.map((item) => item.ns))]; + const langs = makeUniqueList(list.map((item) => item.lang)); + const namespaces = makeUniqueList(list.map((item) => item.ns)); - i18n.reloadResources(langs, namespaces, (error) => { + i18nInstance.reloadResources(langs, namespaces, (error) => { if (error) { log(`\x1b[31m\x1b[1m${error}\x1b[0m\x1b[39m`); } else { @@ -35,7 +36,7 @@ module.exports = function applyServerHMR(i18n) { if (!changedFile || changedFile) { // We must use the required variable - log(`Server HMR has started`); + logOnce(`Server HMR has started`); } module.hot.accept('./trigger.js', () => { @@ -43,7 +44,7 @@ module.exports = function applyServerHMR(i18n) { reloadServerTranslation(changedFiles); }); } else { - log(`Server HMR has started`); + logOnce(`Server HMR has started - callback mode`); const HMRPlugin = require('./plugin'); HMRPlugin.addListener(reloadServerTranslation); diff --git a/lib/utils.js b/lib/utils.js index a56a902..0adc0b5 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,29 +1,61 @@ -module.exports = { - extractLangAndNS: function (changedFile, currentNSList) { - const changedFileParts = changedFile.replace(/\\/g, '/').split('/'); - - const firstLongestNSMatchParts = [] - .concat(currentNSList) - .map((ns) => ns.split('/')) - .sort((a, b) => b.length - a.length) - .find((optionalNS) => - optionalNS.every((optionalNSPart) => changedFileParts.includes(optionalNSPart)) - ); - - if (!firstLongestNSMatchParts) { - return { lang: null, ns: null }; - } +function extractLangAndNS(changedFile, currentNSList) { + const changedFileParts = changedFile.replace(/\\/g, '/').split('/'); - const lang = changedFileParts - .filter((part) => !firstLongestNSMatchParts.includes(part)) - .join('/'); - - return { - lang, - ns: firstLongestNSMatchParts.join('/'), - }; - }, - printList: function (list) { - return list.map(item => `${item.lang}/${item.ns}`).join(', ') + const firstLongestNSMatchParts = [] + .concat(currentNSList) + .map((ns) => ns.split('/')) + .sort((a, b) => b.length - a.length) + .find((optionalNS) => + optionalNS.every((optionalNSPart) => changedFileParts.includes(optionalNSPart)) + ); + + if (!firstLongestNSMatchParts) { + return { lang: null, ns: null }; } + + const lang = changedFileParts + .filter((part) => !firstLongestNSMatchParts.includes(part)) + .join('/'); + + return { + lang, + ns: firstLongestNSMatchParts.join('/'), + }; +} + +function printList(list) { + return list.map((item) => `${item.lang}/${item.ns}`).join(', '); +} + +function extractList(changedFiles, currentNSList) { + return changedFiles + .map((changedFile) => extractLangAndNS(changedFile, currentNSList)) + .filter(({ lang, ns }) => Boolean(lang) && Boolean(ns)); +} + +function makeUniqueList(list) { + return [...new Set(list)]; +} + +function createLoggerOnce(log) { + const msgCount = new Map(); + return (msg, type = 'log') => { + if (!msgCount.has(msg)) { + msgCount.set(msg, 0); + } + + if (msgCount.get(msg) >= 1) { + return; + } + + log(msg, type); + msgCount.set(msg, 1); + }; +} + +module.exports = { + printList: printList, + extractList: extractList, + makeUniqueList: makeUniqueList, + createLoggerOnce: createLoggerOnce, }; diff --git a/server.d.ts b/server.d.ts index 2dc50d5..186d00b 100644 --- a/server.d.ts +++ b/server.d.ts @@ -1,7 +1,7 @@ import i18next from 'i18next'; declare const server: { - applyServerHMR(i18n: i18next.i18n): void; + applyServerHMR(i18nOrGetter: i18next.i18n | (() => i18next.i18n)): void; }; export = server;