Skip to content

Commit 291afb4

Browse files
author
Sergey Melyukov
committed
lint: added unused l10n tokens linter
1 parent 349f3f5 commit 291afb4

File tree

8 files changed

+215
-85
lines changed

8 files changed

+215
-85
lines changed

lib/lint/collectUsed/collector.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = function collectFiles(flow, callback){
2+
var stack = [flow.files.queue[0]];
3+
var collectedFiles = {};
4+
var handled = {};
5+
var cursor;
6+
7+
while (cursor = stack.pop())
8+
{
9+
// mark file as handled
10+
handled[cursor.uniqueId] = true;
11+
callback(cursor);
12+
cursor.linkTo.forEach(function(link){
13+
// prevent handling files that are already handled
14+
if (link[0] && !handled[link[0].uniqueId]) {
15+
stack.push(link[0]);
16+
}
17+
});
18+
}
19+
20+
return collectedFiles;
21+
};

lib/lint/collectUsed/files.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
var collect = require('./collector');
2+
3+
function isTarget(basePath, collectPath, file){
4+
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0;
5+
}
6+
7+
module.exports = function collectUsedFiles(flow){
8+
var options = flow.options;
9+
var basePath = options.base;
10+
var collectPath = flow.files.abspath(basePath, options.warnUnusedFiles);
11+
var usedFiles = {};
12+
13+
collect(flow, function(file){
14+
if (isTarget(basePath, collectPath, file))
15+
usedFiles[file.filename] = true;
16+
17+
if (file.type == 'template')
18+
{
19+
if (file.decl.deps)
20+
{
21+
file.decl.deps.forEach(function(resource){
22+
if (!resource.virtual && isTarget(basePath, collectPath, { filename: resource.url }))
23+
usedFiles[resource.url] = true;
24+
});
25+
}
26+
if (file.decl.l10n)
27+
{
28+
file.decl.l10n.forEach(function(item){
29+
var l10nInfo = item.split('@');
30+
var dictFilename = l10nInfo[1];
31+
32+
if (isTarget(basePath, collectPath, { filename: dictFilename }))
33+
usedFiles[dictFilename] = true;
34+
});
35+
}
36+
if (file.decl.styles)
37+
{
38+
file.decl.styles.forEach(function(style){
39+
if (!style.resource && isTarget(basePath, collectPath, { filename: style.sourceUrl }))
40+
usedFiles[style.sourceUrl] = true;
41+
});
42+
}
43+
}
44+
});
45+
46+
flow.usedFiles = {
47+
basePath: basePath,
48+
collectPath: collectPath,
49+
items: Object.keys(usedFiles)
50+
};
51+
};

lib/lint/collectUsed/l10n.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
function isTarget(flow, basePath, collectPath, file){
2+
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0 && !flow.ignoreWarning(file.filename);
3+
}
4+
5+
module.exports = function collectUsedL10n(flow){
6+
var options = flow.options;
7+
var basePath = options.base;
8+
var collectPath = flow.files.abspath(basePath, options.warnUnusedL10n);
9+
var usedTokens = {};
10+
11+
if (!flow.l10n || !flow.l10n.dictionaries)
12+
return;
13+
14+
for (var dictFilename in flow.l10n.dictionaries)
15+
{
16+
var dict = flow.l10n.dictionaries[dictFilename];
17+
var dictFile = dict.file;
18+
var dictTokens = dict.tokens;
19+
var dictObj = dictFile.jsResourceContent;
20+
var dictUsage = dictObj._meta && dictObj._meta.usage || [];
21+
22+
if (isTarget(flow, basePath, collectPath, { filename: dictFilename }))
23+
{
24+
for (var tokenName in dictTokens)
25+
{
26+
var tokenInfo = dictTokens[tokenName];
27+
28+
usedTokens[dictFilename] = usedTokens[dictFilename] || {};
29+
usedTokens[dictFilename][tokenName] = Boolean(tokenInfo.ref.length || dictUsage.indexOf(tokenName) > -1);
30+
}
31+
}
32+
}
33+
34+
flow.usedL10nTokens = {
35+
basePath: basePath,
36+
collectPath: collectPath,
37+
items: usedTokens
38+
};
39+
};

lib/lint/command.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = clap.create('lint', '[fileOrPreset]')
2121
.option('--silent', 'No any output')
2222

2323
.option('--warn-unused-files <base>', 'Warn about unused files in specified path. Do not use with --js-cut-dev. It might cause incorrect result')
24+
.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')
2425
.option('--filter <filename>', 'Show warnings only for specified file', resolveCwd)
2526
.option('-r, --reporter <name>', 'Reporter console (default), checkstyle, junit',
2627
function(reporter){

lib/lint/index.js

+8-62
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ var extract = require('../extract');
77
var command = require('./command');
88
var chalk = require('chalk');
99
var isChildProcess = typeof process.send == 'function'; // child process has send method
10+
var collectUsed = {
11+
files: require('./collectUsed/files'),
12+
l10n: require('./collectUsed/l10n')
13+
};
1014

1115
if (isChildProcess)
1216
process.on('uncaughtException', function(error){
@@ -91,69 +95,10 @@ function lint(config){
9195
l10nInfo: true
9296
}).concat([
9397
function(flow){
94-
function isTarget(basePath, collectPath, file){
95-
return file.filename && (basePath + file.filename).indexOf(collectPath + '/') === 0;
96-
}
97-
9898
if (options.warnUnusedFiles)
99-
{
100-
var basePath = flow.options.base;
101-
var collectPath = flow.files.abspath(basePath, options.warnUnusedFiles);
102-
var stack = [flow.files.queue[0]];
103-
var usedFiles = {};
104-
var handled = {};
105-
var cursor;
106-
107-
while (cursor = stack.pop())
108-
{
109-
// mark file as handled
110-
handled[cursor.uniqueId] = true;
111-
112-
if (isTarget(basePath, collectPath, cursor))
113-
usedFiles[cursor.filename] = true;
114-
115-
if (cursor.type == 'template')
116-
{
117-
if (cursor.decl.deps)
118-
{
119-
cursor.decl.deps.forEach(function(resource){
120-
if (!resource.virtual && isTarget(basePath, collectPath, { filename: resource.url }))
121-
usedFiles[resource.url] = true;
122-
});
123-
}
124-
if (cursor.decl.l10n)
125-
{
126-
cursor.decl.l10n.forEach(function(item){
127-
var l10nInfo = item.split('@');
128-
var dictFilename = l10nInfo[1] || l10nInfo[0];
129-
130-
if (isTarget(basePath, collectPath, { filename: dictFilename }))
131-
usedFiles[dictFilename] = true;
132-
});
133-
}
134-
if (cursor.decl.styles)
135-
{
136-
cursor.decl.styles.forEach(function(style){
137-
if (!style.resource && isTarget(basePath, collectPath, { filename: style.sourceUrl }))
138-
usedFiles[style.sourceUrl] = true;
139-
});
140-
}
141-
}
142-
143-
cursor.linkTo.forEach(function(link){
144-
// prevent to handling files that already handled
145-
if (link[0] && !handled[link[0].uniqueId]) {
146-
stack.push(link[0]);
147-
}
148-
});
149-
}
150-
151-
flow.usedFiles = {
152-
basePath: basePath,
153-
collectPath: collectPath,
154-
items: Object.keys(usedFiles)
155-
};
156-
}
99+
collectUsed.files(flow);
100+
if (options.warnUnusedL10n)
101+
collectUsed.l10n(flow);
157102
},
158103
function(flow){
159104
flow.result = require('./reporter/process-warns')(flow, options);
@@ -214,6 +159,7 @@ function lint(config){
214159
success: !flow.warns.length,
215160
warnings: flow.warns,
216161
usedFiles: flow.usedFiles,
162+
usedL10nTokens: flow.usedL10nTokens,
217163
result: flow.result
218164
});
219165
});

lib/lint/reporter/collectFiles.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var path = require('path');
22
var fs = require('fs');
33

4-
module.exports = function(base){
4+
module.exports = function(base, ext){
55
var stack = [base];
66
var cursor;
77
var collectedFiles = {};
@@ -27,8 +27,16 @@ module.exports = function(base){
2727
for (var i = 0; i < items.length; i++)
2828
stack.push(path.join(cursor, items[i]));
2929
}
30-
else
31-
collectedFiles[cursor] = true;
30+
else {
31+
if (ext){
32+
var fileExt = path.extname(cursor);
33+
34+
if (fileExt.toLowerCase() === ext.toLowerCase())
35+
collectedFiles[cursor] = true;
36+
}
37+
else
38+
collectedFiles[cursor] = true;
39+
}
3240
}
3341

3442
return collectedFiles;

lib/lint/reporter/parallel-process-warns.js

+43-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module.exports = function(tasks){
44
var result = {};
55
var basePaths = {};
66
var usedFiles = {};
7+
var unusedL10nTokens = {};
78
var collectedFiles = {};
89

910
tasks.forEach(function(task){
@@ -33,6 +34,29 @@ module.exports = function(tasks){
3334
usedFiles[task.result.usedFiles.basePath + filename] = true;
3435
});
3536
}
37+
if (task.result.usedL10nTokens)
38+
{
39+
for (var dictFileName in task.result.usedL10nTokens.items) {
40+
var absDictFilename = task.result.usedL10nTokens.basePath + dictFileName;
41+
var tokenNames = task.result.usedL10nTokens.items[dictFileName];
42+
43+
unusedL10nTokens[absDictFilename] = unusedL10nTokens[absDictFilename] || {};
44+
45+
for (var tokenName in tokenNames)
46+
{
47+
if (tokenNames.hasOwnProperty(tokenName))
48+
{
49+
if (!tokenNames[tokenName])
50+
unusedL10nTokens[absDictFilename][tokenName] = true;
51+
else
52+
delete unusedL10nTokens[absDictFilename][tokenName];
53+
}
54+
}
55+
56+
if (!Object.keys(unusedL10nTokens[absDictFilename]).length)
57+
delete unusedL10nTokens[absDictFilename];
58+
}
59+
}
3660
});
3761

3862
if (Object.keys(basePaths).length)
@@ -51,13 +75,29 @@ module.exports = function(tasks){
5175
for (var unusedName in collectedFiles)
5276
{
5377
unusedName = unusedName.slice(process.cwd().length);
54-
result['unused'] = result['unused'] || [];
55-
result['unused'].push({
78+
result['unused files'] = result['unused files'] || [];
79+
result['unused files'].push({
5680
loc: unusedName,
57-
message: 'Unused file: ' + unusedName
81+
message: unusedName
5882
});
5983
}
6084
}
6185

86+
//console.log(unusedL10nTokens)
87+
if (Object.keys(unusedL10nTokens).length) {
88+
for (var dictFileName in unusedL10nTokens){
89+
var tokenNames = unusedL10nTokens[dictFileName];
90+
91+
for (var tokenName in tokenNames)
92+
{
93+
result['unused l10n tokens'] = result['unused l10n tokens'] || [];
94+
result['unused l10n tokens'].push({
95+
loc: dictFileName,
96+
message: 'Unused token: ' + tokenName + ' at ' + dictFileName
97+
});
98+
}
99+
}
100+
}
101+
62102
return result;
63103
};

lib/lint/reporter/process-warns.js

+41-17
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,52 @@ module.exports = function(flow, options){
2121
});
2222
});
2323

24-
if (!isChildProcess && options.warnUnusedFiles && flow.usedFiles)
24+
if (!isChildProcess)
2525
{
26-
var usedFilesInfo = flow.usedFiles;
27-
var basePath = usedFilesInfo.collectPath;
28-
var usedFiles = {};
29-
var collectedFiles = collectFiles(basePath);
26+
var basePath;
27+
var collectedFiles;
3028

31-
usedFilesInfo.items.forEach(function(filename){
32-
usedFiles[usedFilesInfo.basePath + filename] = true;
33-
});
29+
if (options.warnUnusedFiles && flow.usedFiles)
30+
{
31+
var usedFilesInfo = flow.usedFiles;
32+
var usedFiles = {};
3433

35-
for (var usedFile in usedFiles)
36-
delete collectedFiles[usedFile];
34+
basePath = usedFilesInfo.collectPath;
35+
collectedFiles = collectFiles(basePath);
3736

38-
for (var unusedName in collectedFiles)
39-
{
40-
unusedName = unusedName.slice(process.cwd().length);
41-
result['unused'] = result['unused'] || [];
42-
result['unused'].push({
43-
loc: unusedName,
44-
message: 'Unused file: ' + unusedName
37+
usedFilesInfo.items.forEach(function(filename){
38+
usedFiles[usedFilesInfo.basePath + filename] = true;
4539
});
40+
41+
for (var usedFile in usedFiles)
42+
delete collectedFiles[usedFile];
43+
44+
for (var unusedName in collectedFiles) {
45+
unusedName = unusedName.slice(process.cwd().length);
46+
result['unused files'] = result['unused files'] || [];
47+
result['unused files'].push({
48+
loc: unusedName,
49+
message: unusedName
50+
});
51+
}
52+
}
53+
if (options.warnL10nFiles && flow.usedL10nTokens)
54+
{
55+
var usedL10nTokensInfo = flow.usedL10nTokens;
56+
57+
for (var dictFileName in usedL10nTokensInfo.items){
58+
var tokenNames = usedL10nTokensInfo.items[dictFileName];
59+
60+
for (var tokenName in tokenNames) {
61+
if (!tokenNames[tokenName]) {
62+
result[dictFileName] = result[dictFileName] || [];
63+
result[dictFileName].push({
64+
loc: dictFileName,
65+
message: 'Unused token: ' + tokenName
66+
});
67+
}
68+
}
69+
}
4670
}
4771
}
4872

0 commit comments

Comments
 (0)