From 33cb0433e1c0d8b6a9e66db31c67a4330bffc740 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 17 Mar 2022 10:07:23 +0100 Subject: [PATCH] Add support for translations in actual addons --- .gitignore | 3 ++ README.md | 20 ++++++++ __snapshots__/test.js.snap | 16 ++++++ .../app/controllers/application.js | 7 +++ .../app/templates/application.hbs | 4 ++ .../scoped-addon/templates/application.hbs | 1 + .../scoped-addon/translations/en.yaml | 4 ++ .../external-addon/templates/application.hbs | 1 + .../external-addon/translations/en.yaml | 3 ++ .../other-external-addon/translations/en.yaml | 2 + .../translations/en.yaml | 2 + index.js | 49 ++++++++++++++++--- test.js | 7 +++ 13 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 fixtures/external-addon-translations/app/controllers/application.js create mode 100644 fixtures/external-addon-translations/app/templates/application.hbs create mode 100644 fixtures/external-addon-translations/node_modules/@company/scoped-addon/templates/application.hbs create mode 100644 fixtures/external-addon-translations/node_modules/@company/scoped-addon/translations/en.yaml create mode 100644 fixtures/external-addon-translations/node_modules/external-addon/templates/application.hbs create mode 100644 fixtures/external-addon-translations/node_modules/external-addon/translations/en.yaml create mode 100644 fixtures/external-addon-translations/node_modules/other-external-addon/translations/en.yaml create mode 100644 fixtures/external-addon-translations/translations/en.yaml diff --git a/.gitignore b/.gitignore index eeab00af..4a9c2108 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ build/Release node_modules/ jspm_packages/ +# Include node modules used in tests +!fixtures/**/node_modules + # TypeScript v1 declaration files typings/ diff --git a/README.md b/README.md index 6d0ead3d..0483e9e0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,26 @@ To prevent that from happening you can configure a `whitelist`, which accepts an array of regular expressions that will be checked when looking for unused translations. +### `externalPaths` + +If your application uses translations provided by (external) addons, then those +translations will show up as missing by default. In order to include such translations, +you can define `externalPaths` in the configuration file as follows: + +```js +export default { + externalPaths: ['my-addon'], +}; +``` + +This example will try to find translation files in `node_modules/my-addon/translations`. +Patterns supported by [`globby`](https://www.npmjs.com/package/globby) are also +possible here, e.g. this: +```js +externalPaths: ['@*/*'] +``` +will look up translations in scoped addons like `node_modules/@company/scoped-addon/translations`. + ### `--fix` If your application has a lot of unused translations you can run the command with the `--fix` to remove them. Remember to double check your translations as dynamic diff --git a/__snapshots__/test.js.snap b/__snapshots__/test.js.snap index 3bb02842..638c96fd 100644 --- a/__snapshots__/test.js.snap +++ b/__snapshots__/test.js.snap @@ -44,6 +44,22 @@ exports[`Test Fixtures emblem 1`] = ` exports[`Test Fixtures emblem 2`] = `Map {}`; +exports[`Test Fixtures external-addon-translations 1`] = ` +"[1/4] 🔍 Finding JS and HBS files... +[2/4] 🔍 Searching for translations keys in JS and HBS files... +[3/4] ⚙️ Checking for unused translations... +[4/4] ⚙️ Checking for missing translations... + + 👏 No unused translations were found! + + ⚠️ Found 1 missing translations! + + - other-external-addon.used-by-app-translation (used in app/templates/application.hbs) +" +`; + +exports[`Test Fixtures external-addon-translations 2`] = `Map {}`; + exports[`Test Fixtures in-repo-translations 1`] = ` "[1/4] 🔍 Finding JS and HBS files... [2/4] 🔍 Searching for translations keys in JS and HBS files... diff --git a/fixtures/external-addon-translations/app/controllers/application.js b/fixtures/external-addon-translations/app/controllers/application.js new file mode 100644 index 00000000..468eb331 --- /dev/null +++ b/fixtures/external-addon-translations/app/controllers/application.js @@ -0,0 +1,7 @@ +import Controller from '@ember/controller'; + +export default class ApplicationController extends Controller { + get foo() { + return this.intl.t('js-translation'); + } +} diff --git a/fixtures/external-addon-translations/app/templates/application.hbs b/fixtures/external-addon-translations/app/templates/application.hbs new file mode 100644 index 00000000..57b144f5 --- /dev/null +++ b/fixtures/external-addon-translations/app/templates/application.hbs @@ -0,0 +1,4 @@ +{{t "hbs-translation"}} +{{t "external-addon.used-by-app-translation"}} +{{t "other-external-addon.used-by-app-translation"}} +{{t "company.scoped-addon.used-by-app-translation"}} diff --git a/fixtures/external-addon-translations/node_modules/@company/scoped-addon/templates/application.hbs b/fixtures/external-addon-translations/node_modules/@company/scoped-addon/templates/application.hbs new file mode 100644 index 00000000..490e0dd5 --- /dev/null +++ b/fixtures/external-addon-translations/node_modules/@company/scoped-addon/templates/application.hbs @@ -0,0 +1 @@ +{{t "scoped-addon.missing.but.unmarked-translation"}} diff --git a/fixtures/external-addon-translations/node_modules/@company/scoped-addon/translations/en.yaml b/fixtures/external-addon-translations/node_modules/@company/scoped-addon/translations/en.yaml new file mode 100644 index 00000000..6bcbc1cd --- /dev/null +++ b/fixtures/external-addon-translations/node_modules/@company/scoped-addon/translations/en.yaml @@ -0,0 +1,4 @@ +company: + scoped-addon: + used-by-app-translation: This translation is used by app, and is not marked missing + unused-by-app-translation: This translation is not used by app, but is not marked unused, because it's external diff --git a/fixtures/external-addon-translations/node_modules/external-addon/templates/application.hbs b/fixtures/external-addon-translations/node_modules/external-addon/templates/application.hbs new file mode 100644 index 00000000..fe32f55c --- /dev/null +++ b/fixtures/external-addon-translations/node_modules/external-addon/templates/application.hbs @@ -0,0 +1 @@ +{{t "external-addon.missing.but.unmarked-translation"}} diff --git a/fixtures/external-addon-translations/node_modules/external-addon/translations/en.yaml b/fixtures/external-addon-translations/node_modules/external-addon/translations/en.yaml new file mode 100644 index 00000000..5defc244 --- /dev/null +++ b/fixtures/external-addon-translations/node_modules/external-addon/translations/en.yaml @@ -0,0 +1,3 @@ +external-addon: + used-by-app-translation: This translation is used by app, and is not marked missing + unused-by-app-translation: This translation is not used by app, but is not marked unused, because it's external diff --git a/fixtures/external-addon-translations/node_modules/other-external-addon/translations/en.yaml b/fixtures/external-addon-translations/node_modules/other-external-addon/translations/en.yaml new file mode 100644 index 00000000..3f52699f --- /dev/null +++ b/fixtures/external-addon-translations/node_modules/other-external-addon/translations/en.yaml @@ -0,0 +1,2 @@ +other-external-addon: + used-by-app-translation: This translation is used by app, but not included in the config diff --git a/fixtures/external-addon-translations/translations/en.yaml b/fixtures/external-addon-translations/translations/en.yaml new file mode 100644 index 00000000..91079a90 --- /dev/null +++ b/fixtures/external-addon-translations/translations/en.yaml @@ -0,0 +1,2 @@ +hbs-translation: HBS! +js-translation: JS! diff --git a/index.js b/index.js index 683a5177..1b4fa6dc 100755 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ async function run(rootDir, options = {}) { const NUM_STEPS = 4; const step = num => chalk.dim(`[${num}/${NUM_STEPS}]`); - let config = readConfig(rootDir); + let config = options.config || readConfig(rootDir); log(`${step(1)} 🔍 Finding JS and HBS files...`); let appFiles = await findAppFiles(rootDir); @@ -37,12 +37,21 @@ async function run(rootDir, options = {}) { log(`${step(3)} ⚙️ Checking for unused translations...`); - let translationFiles = await findTranslationFiles(rootDir); - let existingTranslationKeys = await analyzeTranslationFiles(rootDir, translationFiles); + let ownTranslationFiles = await findOwnTranslationFiles(rootDir); + let externalTranslationFiles = await findExternalTranslationFiles(rootDir, config); + let existingOwnTranslationKeys = await analyzeTranslationFiles(rootDir, ownTranslationFiles); + let existingExternalTranslationKeys = await analyzeTranslationFiles( + rootDir, + externalTranslationFiles + ); + let existingTranslationKeys = mergeMaps( + existingOwnTranslationKeys, + existingExternalTranslationKeys + ); let whitelist = config.whitelist || []; let unusedTranslations = findDifferenceInTranslations( - existingTranslationKeys, + existingOwnTranslationKeys, usedTranslationKeys, whitelist ); @@ -82,7 +91,7 @@ async function run(rootDir, options = {}) { let totalErrors = missingTranslations.size + unusedTranslations.size; if (shouldFix) { - removeUnusedTranslations(writeToFile, rootDir, translationFiles, unusedTranslations); + removeUnusedTranslations(writeToFile, rootDir, ownTranslationFiles, unusedTranslations); log(); log(' 👏 All unused translations were removed'); } @@ -113,8 +122,19 @@ async function findInRepoFiles(cwd) { return globby(joinPaths(inRepoFolders, ['**/*.js', '**/*.hbs', '**/*.emblem']), { cwd }); } -async function findTranslationFiles(cwd) { - let inputFolders = ['', ...findInRepoPaths(cwd)]; +async function findOwnTranslationFiles(cwd) { + return findTranslationFiles(cwd, ['', ...findInRepoPaths(cwd)]); +} + +async function findExternalTranslationFiles(cwd, config) { + if (!config.externalPaths) { + return []; + } + + return findTranslationFiles(cwd, joinPaths('node_modules', config.externalPaths)); +} + +async function findTranslationFiles(cwd, inputFolders) { let translationPaths = joinPaths(inputFolders, ['translations']); return globby(joinPaths(translationPaths, ['**/*.json', '**/*.yaml', '**/*.yml']), { @@ -388,4 +408,19 @@ function getNestedAttribute(parent, keys) { return attribute; } +function mergeMaps(mapA, mapB) { + let resultMap = new Map([...mapA]); + + for (let [key, bFiles] of mapB) { + if (!resultMap.has(key)) { + resultMap.set(key, bFiles); + } else { + let aFiles = resultMap.get(key); + resultMap.set(key, new Set([...aFiles, ...bFiles])); + } + } + + return resultMap; +} + module.exports = { run, generateFileList }; diff --git a/test.js b/test.js index 5a830897..9a586ae7 100644 --- a/test.js +++ b/test.js @@ -11,8 +11,14 @@ describe('Test Fixtures', () => { 'missing-translations', 'unused-translations', 'in-repo-translations', + 'external-addon-translations', ]; let fixturesWithFix = ['remove-unused-translations', 'remove-unused-translations-nested']; + let fixturesWithConfig = { + 'external-addon-translations': { + externalPaths: ['@*/*', 'external-addon'], + }, + }; beforeEach(() => { output = ''; @@ -34,6 +40,7 @@ describe('Test Fixtures', () => { fix: fixturesWithFix.includes(fixture), color: false, writeToFile, + config: fixturesWithConfig[fixture], }); let expectedReturnValue = fixturesWithErrors.includes(fixture) ? 1 : 0;