Skip to content

Commit

Permalink
lint: added unused l10n tokens linter
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Melyukov committed Jun 16, 2017
1 parent 349f3f5 commit b241c96
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 85 deletions.
21 changes: 21 additions & 0 deletions lib/lint/collectUsed/collector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = function collectFiles(flow, callback){
var stack = [flow.files.queue[0]];
var collectedFiles = {};
var handled = {};
var cursor;

while (cursor = stack.pop())
{
// mark file as handled
handled[cursor.uniqueId] = true;
callback(cursor);
cursor.linkTo.forEach(function(link){
// prevent handling files that are already handled
if (link[0] && !handled[link[0].uniqueId]) {
stack.push(link[0]);
}
});
}

return collectedFiles;
};
51 changes: 51 additions & 0 deletions lib/lint/collectUsed/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
var collect = require('./collector');

function isTarget(basePath, collectPath, file){
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0;
}

module.exports = function collectUsedFiles(flow){
var options = flow.options;
var basePath = options.base;
var collectPath = flow.files.abspath(basePath, options.warnUnusedFiles);
var usedFiles = {};

collect(flow, function(file){
if (isTarget(basePath, collectPath, file))
usedFiles[file.filename] = true;

if (file.type == 'template')
{
if (file.decl.deps)
{
file.decl.deps.forEach(function(resource){
if (!resource.virtual && isTarget(basePath, collectPath, { filename: resource.url }))
usedFiles[resource.url] = true;
});
}
if (file.decl.l10n)
{
file.decl.l10n.forEach(function(item){
var l10nInfo = item.split('@');
var dictFilename = l10nInfo[1];

if (isTarget(basePath, collectPath, { filename: dictFilename }))
usedFiles[dictFilename] = true;
});
}
if (file.decl.styles)
{
file.decl.styles.forEach(function(style){
if (!style.resource && isTarget(basePath, collectPath, { filename: style.sourceUrl }))
usedFiles[style.sourceUrl] = true;
});
}
}
});

flow.usedFiles = {
basePath: basePath,
collectPath: collectPath,
items: Object.keys(usedFiles)
};
};
39 changes: 39 additions & 0 deletions lib/lint/collectUsed/l10n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
function isTarget(flow, basePath, collectPath, file){
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0 && !flow.ignoreWarning(file.filename);
}

module.exports = function collectUsedL10n(flow){
var options = flow.options;
var basePath = options.base;
var collectPath = flow.files.abspath(basePath, options.warnUnusedL10n);
var usedTokens = {};

if (!flow.l10n || !flow.l10n.dictionaries)
return;

for (var dictFilename in flow.l10n.dictionaries)
{
var dict = flow.l10n.dictionaries[dictFilename];
var dictFile = dict.file;
var dictTokens = dict.tokens;
var dictObj = dictFile.jsResourceContent;
var dictUsage = dictObj._meta && dictObj._meta.usage || [];

if (isTarget(flow, basePath, collectPath, { filename: dictFilename }))
{
for (var tokenName in dictTokens)
{
var tokenInfo = dictTokens[tokenName];

usedTokens[dictFilename] = usedTokens[dictFilename] || {};
usedTokens[dictFilename][tokenName] = Boolean(tokenInfo.ref.length || dictUsage.indexOf(tokenName) > -1);
}
}
}

flow.usedL10nTokens = {
basePath: basePath,
collectPath: collectPath,
items: usedTokens
};
};
1 change: 1 addition & 0 deletions lib/lint/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = clap.create('lint', '[fileOrPreset]')
.option('--silent', 'No any output')

.option('--warn-unused-files <base>', 'Warn about unused files in specified path. Do not use with --js-cut-dev. It might cause incorrect result')
.option('--warn-unused-l10n <base>', 'Warn about unused l10n tokens in specified path. Do not use with --js-cut-dev. It might cause incorrect result')
.option('--filter <filename>', 'Show warnings only for specified file', resolveCwd)
.option('-r, --reporter <name>', 'Reporter console (default), checkstyle, junit',
function(reporter){
Expand Down
70 changes: 8 additions & 62 deletions lib/lint/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ var extract = require('../extract');
var command = require('./command');
var chalk = require('chalk');
var isChildProcess = typeof process.send == 'function'; // child process has send method
var collectUsed = {
files: require('./collectUsed/files'),
l10n: require('./collectUsed/l10n')
};

if (isChildProcess)
process.on('uncaughtException', function(error){
Expand Down Expand Up @@ -91,69 +95,10 @@ function lint(config){
l10nInfo: true
}).concat([
function(flow){
function isTarget(basePath, collectPath, file){
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0;
}

if (options.warnUnusedFiles)
{
var basePath = flow.options.base;
var collectPath = flow.files.abspath(basePath, options.warnUnusedFiles);
var stack = [flow.files.queue[0]];
var usedFiles = {};
var handled = {};
var cursor;

while (cursor = stack.pop())
{
// mark file as handled
handled[cursor.uniqueId] = true;

if (isTarget(basePath, collectPath, cursor))
usedFiles[cursor.filename] = true;

if (cursor.type == 'template')
{
if (cursor.decl.deps)
{
cursor.decl.deps.forEach(function(resource){
if (!resource.virtual && isTarget(basePath, collectPath, { filename: resource.url }))
usedFiles[resource.url] = true;
});
}
if (cursor.decl.l10n)
{
cursor.decl.l10n.forEach(function(item){
var l10nInfo = item.split('@');
var dictFilename = l10nInfo[1] || l10nInfo[0];

if (isTarget(basePath, collectPath, { filename: dictFilename }))
usedFiles[dictFilename] = true;
});
}
if (cursor.decl.styles)
{
cursor.decl.styles.forEach(function(style){
if (!style.resource && isTarget(basePath, collectPath, { filename: style.sourceUrl }))
usedFiles[style.sourceUrl] = true;
});
}
}

cursor.linkTo.forEach(function(link){
// prevent to handling files that already handled
if (link[0] && !handled[link[0].uniqueId]) {
stack.push(link[0]);
}
});
}

flow.usedFiles = {
basePath: basePath,
collectPath: collectPath,
items: Object.keys(usedFiles)
};
}
collectUsed.files(flow);
if (options.warnUnusedL10n)
collectUsed.l10n(flow);
},
function(flow){
flow.result = require('./reporter/process-warns')(flow, options);
Expand Down Expand Up @@ -214,6 +159,7 @@ function lint(config){
success: !flow.warns.length,
warnings: flow.warns,
usedFiles: flow.usedFiles,
usedL10nTokens: flow.usedL10nTokens,
result: flow.result
});
});
Expand Down
14 changes: 11 additions & 3 deletions lib/lint/reporter/collectFiles.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var path = require('path');
var fs = require('fs');

module.exports = function(base){
module.exports = function(base, ext){
var stack = [base];
var cursor;
var collectedFiles = {};
Expand All @@ -27,8 +27,16 @@ module.exports = function(base){
for (var i = 0; i < items.length; i++)
stack.push(path.join(cursor, items[i]));
}
else
collectedFiles[cursor] = true;
else {
if (ext){
var fileExt = path.extname(cursor);

if (fileExt.toLowerCase() === ext.toLowerCase())
collectedFiles[cursor] = true;
}
else
collectedFiles[cursor] = true;
}
}

return collectedFiles;
Expand Down
48 changes: 45 additions & 3 deletions lib/lint/reporter/parallel-process-warns.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = function(tasks){
var result = {};
var basePaths = {};
var usedFiles = {};
var unusedL10nTokens = {};
var collectedFiles = {};

tasks.forEach(function(task){
Expand Down Expand Up @@ -33,6 +34,29 @@ module.exports = function(tasks){
usedFiles[task.result.usedFiles.basePath + filename] = true;
});
}
if (task.result.usedL10nTokens)
{
for (var dictFileName in task.result.usedL10nTokens.items) {
var absDictFilename = task.result.usedL10nTokens.basePath + dictFileName;
var tokenNames = task.result.usedL10nTokens.items[dictFileName];

unusedL10nTokens[absDictFilename] = unusedL10nTokens[absDictFilename] || {};

for (var tokenName in tokenNames)
{
if (tokenNames.hasOwnProperty(tokenName))
{
if (!tokenNames[tokenName])
unusedL10nTokens[absDictFilename][tokenName] = true;
else
delete unusedL10nTokens[absDictFilename][tokenName];
}
}

if (!Object.keys(unusedL10nTokens[absDictFilename]).length)
delete unusedL10nTokens[absDictFilename];
}
}
});

if (Object.keys(basePaths).length)
Expand All @@ -51,13 +75,31 @@ module.exports = function(tasks){
for (var unusedName in collectedFiles)
{
unusedName = unusedName.slice(process.cwd().length);
result['unused'] = result['unused'] || [];
result['unused'].push({
result['unused files'] = result['unused files'] || [];
result['unused files'].push({
loc: unusedName,
message: 'Unused file: ' + unusedName
message: unusedName
});
}
}

//console.log(unusedL10nTokens)
if (Object.keys(unusedL10nTokens).length) {
for (var dictFileName in unusedL10nTokens){
var tokenNames = unusedL10nTokens[dictFileName];

dictFileName = dictFileName.slice(process.cwd().length);

for (var tokenName in tokenNames)
{
result['unused l10n tokens'] = result['unused l10n tokens'] || [];
result['unused l10n tokens'].push({
loc: dictFileName,
message: 'Unused token: ' + tokenName + ' at ' + dictFileName
});
}
}
}

return result;
};
58 changes: 41 additions & 17 deletions lib/lint/reporter/process-warns.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,52 @@ module.exports = function(flow, options){
});
});

if (!isChildProcess && options.warnUnusedFiles && flow.usedFiles)
if (!isChildProcess)
{
var usedFilesInfo = flow.usedFiles;
var basePath = usedFilesInfo.collectPath;
var usedFiles = {};
var collectedFiles = collectFiles(basePath);
var basePath;
var collectedFiles;

usedFilesInfo.items.forEach(function(filename){
usedFiles[usedFilesInfo.basePath + filename] = true;
});
if (options.warnUnusedFiles && flow.usedFiles)
{
var usedFilesInfo = flow.usedFiles;
var usedFiles = {};

for (var usedFile in usedFiles)
delete collectedFiles[usedFile];
basePath = usedFilesInfo.collectPath;
collectedFiles = collectFiles(basePath);

for (var unusedName in collectedFiles)
{
unusedName = unusedName.slice(process.cwd().length);
result['unused'] = result['unused'] || [];
result['unused'].push({
loc: unusedName,
message: 'Unused file: ' + unusedName
usedFilesInfo.items.forEach(function(filename){
usedFiles[usedFilesInfo.basePath + filename] = true;
});

for (var usedFile in usedFiles)
delete collectedFiles[usedFile];

for (var unusedName in collectedFiles) {
unusedName = unusedName.slice(process.cwd().length);
result['unused files'] = result['unused files'] || [];
result['unused files'].push({
loc: unusedName,
message: unusedName
});
}
}
if (options.warnUnusedL10n && flow.usedL10nTokens)
{
var usedL10nTokensInfo = flow.usedL10nTokens;

for (var dictFileName in usedL10nTokensInfo.items){
var tokenNames = usedL10nTokensInfo.items[dictFileName];

for (var tokenName in tokenNames) {
if (!tokenNames[tokenName]) {
result[dictFileName] = result[dictFileName] || [];
result[dictFileName].push({
loc: dictFileName,
message: 'Unused token: ' + tokenName
});
}
}
}
}
}

Expand Down

0 comments on commit b241c96

Please sign in to comment.