Skip to content

Commit

Permalink
Add option to log dynamic translation lookups
Browse files Browse the repository at this point in the history
  • Loading branch information
robinborst95 committed May 26, 2022
1 parent ec41a75 commit ac0e717
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
5 changes: 5 additions & 0 deletions __snapshots__/test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
exports[`Test Fixtures concat-expression 1`] = `
"[1/4] 🔍 Finding JS and HBS files...
[2/4] 🔍 Searching for translations keys in JS and HBS files...
⭐ Found 2 dynamic translations. This might cause this tool to report more unused translations than there actually are!
- prefix.{{this.dynamicKey}}.not-missing (used in app/templates/application.hbs)
- prefix.{{this.dynamicKey}}.value (used in app/templates/application.hbs)
[3/4] ⚙️ Checking for unused translations...
[4/4] ⚙️ Checking for missing translations...
Expand Down
5 changes: 4 additions & 1 deletion bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const { run } = require('../index');

let rootDir = pkgDir.sync();

run(rootDir, { fix: process.argv.includes('--fix') })
run(rootDir, {
fix: process.argv.includes('--fix'),
logDynamic: process.argv.includes('--log-dynamic'),
})
.then(exitCode => {
process.exitCode = exitCode;
})
Expand Down
109 changes: 98 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ async function run(rootDir, options = {}) {
let log = options.log || console.log;
let writeToFile = options.writeToFile || fs.writeFileSync;
let shouldFix = options.fix || false;
let logDynamic = options.logDynamic || false;

let chalkOptions = {};
if (options.color === false) {
Expand All @@ -33,7 +34,26 @@ async function run(rootDir, options = {}) {
let files = [...appFiles, ...inRepoFiles];

log(`${step(2)} 🔍 Searching for translations keys in JS and HBS files...`);
let usedTranslationKeys = await analyzeFiles(rootDir, files);
let [usedTranslationKeys, usedDynamicTranslations] = await analyzeFiles(rootDir, files);

if (logDynamic) {
if (usedDynamicTranslations.size === 0) {
log();
log(' ⭐ No dynamic translations were found.');
log();
} else {
log();
log(
` ⭐ Found ${chalk.bold.yellow(
usedDynamicTranslations.size
)} dynamic translations. This might cause this tool to report more unused translations than there actually are!`
);
log();
for (let [key, files] of usedDynamicTranslations) {
log(` - ${key} ${chalk.dim(`(used in ${generateFileList(files)})`)}`);
}
}
}

log(`${step(3)} ⚙️ Checking for unused translations...`);

Expand Down Expand Up @@ -163,9 +183,10 @@ function joinPaths(inputPathOrPaths, outputPaths) {

async function analyzeFiles(cwd, files) {
let allTranslationKeys = new Map();
let allDynamicTranslations = new Map();

for (let file of files) {
let translationKeys = await analyzeFile(cwd, file);
let [translationKeys, dynamicTranslations] = await analyzeFile(cwd, file);

for (let key of translationKeys) {
if (allTranslationKeys.has(key)) {
Expand All @@ -174,9 +195,17 @@ async function analyzeFiles(cwd, files) {
allTranslationKeys.set(key, new Set([file]));
}
}

for (let dynamicTranslation of dynamicTranslations) {
if (allDynamicTranslations.has(dynamicTranslation)) {
allDynamicTranslations.get(dynamicTranslation).add(file);
} else {
allDynamicTranslations.set(dynamicTranslation, new Set([file]));
}
}
}

return allTranslationKeys;
return [allTranslationKeys, allDynamicTranslations];
}

async function analyzeFile(cwd, file) {
Expand Down Expand Up @@ -229,24 +258,78 @@ async function analyzeJsFile(content) {
},
});

return translationKeys;
return [translationKeys, new Set()];
}

async function analyzeHbsFile(content) {
let translationKeys = new Set();
let dynamicTranslations = new Set();

// parse the HBS file
let ast = Glimmer.preprocess(content);

class StringKey {
constructor(value) {
this.value = value;
}

join(otherKey) {
if (otherKey instanceof StringKey) {
return new StringKey(this.value + otherKey.value);
} else {
return new CompositeKey(this, otherKey);
}
}

toString() {
return this.value;
}
}

class DynamicKey {
constructor(node) {
this.node = node;
}

join(otherKey) {
return new CompositeKey(this, otherKey);
}

toString() {
if (this.node.type === 'PathExpression') {
return `{{${this.node.original}}}`;
} else if (this.node.type === 'SubExpression') {
return `{{${this.node.path.original} helper}}`;
}

return '{{dynamic key}}';
}
}

class CompositeKey {
constructor(...values) {
this.values = values;
}

join(otherKey) {
return new CompositeKey(...this.values, otherKey);
}

toString() {
return this.values.reduce((string, value) => string + value.toString(), '');
}
}

function findKeysInIfExpression(node) {
let keysInFirstParam = findKeysInNode(node.params[1]);
let keysInSecondParam = node.params.length > 2 ? findKeysInNode(node.params[2]) : [''];
let keysInSecondParam =
node.params.length > 2 ? findKeysInNode(node.params[2]) : [new StringKey('')];

return [...keysInFirstParam, ...keysInSecondParam];
}

function findKeysInConcatExpression(node) {
let potentialKeys = [''];
let potentialKeys = [new StringKey('')];

for (let param of node.params) {
let keysInParam = findKeysInNode(param);
Expand All @@ -255,7 +338,7 @@ async function analyzeHbsFile(content) {

potentialKeys = potentialKeys.reduce((newPotentialKeys, potentialKey) => {
for (let key of keysInParam) {
newPotentialKeys.push(potentialKey + key);
newPotentialKeys.push(potentialKey.join(key));
}

return newPotentialKeys;
Expand All @@ -269,14 +352,14 @@ async function analyzeHbsFile(content) {
if (!node) return [];

if (node.type === 'StringLiteral') {
return [node.value];
return [new StringKey(node.value)];
} else if (node.type === 'SubExpression' && node.path.original === 'if') {
return findKeysInIfExpression(node);
} else if (node.type === 'SubExpression' && node.path.original === 'concat') {
return findKeysInConcatExpression(node);
}

return [];
return [new DynamicKey(node)];
}

function processNode(node) {
Expand All @@ -285,7 +368,11 @@ async function analyzeHbsFile(content) {
if (node.params.length === 0) return;

for (let key of findKeysInNode(node.params[0])) {
translationKeys.add(key);
if (key instanceof StringKey) {
translationKeys.add(key.value);
} else {
dynamicTranslations.add(key.toString());
}
}
}

Expand All @@ -302,7 +389,7 @@ async function analyzeHbsFile(content) {
},
});

return translationKeys;
return [translationKeys, dynamicTranslations];
}

async function analyzeTranslationFiles(cwd, files) {
Expand Down
2 changes: 2 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('Test Fixtures', () => {
'external-addon-translations',
];
let fixturesWithFix = ['remove-unused-translations', 'remove-unused-translations-nested'];
let fixturesWithDynamicKeys = ['concat-expression'];
let fixturesWithConfig = {
'external-addon-translations': {
externalPaths: ['@*/*', 'external-addon'],
Expand Down Expand Up @@ -41,6 +42,7 @@ describe('Test Fixtures', () => {
color: false,
writeToFile,
config: fixturesWithConfig[fixture],
logDynamic: fixturesWithDynamicKeys.includes(fixture),
});

let expectedReturnValue = fixturesWithErrors.includes(fixture) ? 1 : 0;
Expand Down

0 comments on commit ac0e717

Please sign in to comment.