Skip to content

Commit d14cd05

Browse files
Chore: unify logic for parsing context.report() arguments
1 parent 2fa5e7f commit d14cd05

File tree

4 files changed

+91
-33
lines changed

4 files changed

+91
-33
lines changed

Diff for: lib/rules/no-deprecated-report-api.js

+3-20
Original file line numberDiff line numberDiff line change
@@ -47,32 +47,15 @@ module.exports = {
4747
fix (fixer) {
4848
const openingParen = sourceCode.getTokenBefore(node.arguments[0]);
4949
const closingParen = sourceCode.getLastToken(node);
50+
const reportInfo = utils.getReportInfo(node.arguments);
5051

51-
// If there is exactly one argument, the API expects an object.
52-
// Otherwise, if the second argument is a string, the arguments are interpreted as
53-
// ['node', 'message', 'data', 'fix'].
54-
// Otherwise, the arguments are interpreted as ['node', 'loc', 'message', 'data', 'fix'].
55-
56-
let keys;
57-
if (
58-
(node.arguments[1].type === 'Literal' && typeof node.arguments[1].value === 'string') ||
59-
node.arguments[1].type === 'TemplateLiteral'
60-
) {
61-
keys = ['node', 'message', 'data', 'fix'];
62-
} else if (
63-
node.arguments[1].type === 'ObjectExpression' ||
64-
node.arguments[1].type === 'ArrayExpression' ||
65-
(node.arguments[1].type === 'Literal' && typeof node.arguments[1].value !== 'string')
66-
) {
67-
keys = ['node', 'loc', 'message', 'data', 'fix'];
68-
} else {
69-
// Otherwise, we can't statically determine what argument means what, so no safe fix is possible.
52+
if (!reportInfo) {
7053
return null;
7154
}
7255

7356
return fixer.replaceTextRange(
7457
[openingParen.range[1], closingParen.range[0]],
75-
`{${node.arguments.slice(0, 5).map((arg, index) => `${keys[index]}: ${sourceCode.getText(arg)}`).join(', ')}}`
58+
`{${Object.keys(reportInfo).map(key => `${key}: ${sourceCode.getText(reportInfo[key])}`).join(', ')}}`
7659
);
7760
},
7861
});

Diff for: lib/rules/report-message-format.js

+4-13
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,11 @@ module.exports = {
4242
contextIdentifiers.has(node.callee.object) &&
4343
node.callee.property.type === 'Identifier' && node.callee.property.name === 'report'
4444
) {
45-
if (!node.arguments.length || (node.arguments.length === 1 && node.arguments[0].type !== 'ObjectExpression')) {
46-
return;
47-
}
45+
const reportInfo = utils.getReportInfo(node.arguments);
46+
const message = reportInfo && reportInfo.message;
4847

49-
let message;
50-
51-
if (node.arguments.length === 1) {
52-
const messageProp = node.arguments[0].properties.find(prop => utils.getKeyName(prop) === 'message');
53-
if (!messageProp) {
54-
return;
55-
}
56-
message = messageProp.value;
57-
} else {
58-
message = node.arguments[1];
48+
if (!message) {
49+
return;
5950
}
6051

6152
if (

Diff for: lib/utils.js

+51
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,55 @@ module.exports = {
184184
};
185185
});
186186
},
187+
188+
/**
189+
* Gets information on a report, given the arguments passed to context.report().
190+
* @param {ASTNode[]} reportArgs The arguments passed to context.report()
191+
*/
192+
getReportInfo (reportArgs) {
193+
// If there is exactly one argument, the API expects an object.
194+
// Otherwise, if the second argument is a string, the arguments are interpreted as
195+
// ['node', 'message', 'data', 'fix'].
196+
// Otherwise, the arguments are interpreted as ['node', 'loc', 'message', 'data', 'fix'].
197+
198+
if (!reportArgs.length) {
199+
return null;
200+
}
201+
202+
if (reportArgs.length === 1) {
203+
if (reportArgs[0].type === 'ObjectExpression') {
204+
return reportArgs[0].properties.reduce((reportInfo, property) => {
205+
const propName = module.exports.getKeyName(property);
206+
207+
if (propName !== null) {
208+
return Object.assign(reportInfo, { [propName]: property.value });
209+
}
210+
return reportInfo;
211+
}, {});
212+
}
213+
return null;
214+
}
215+
216+
let keys;
217+
218+
if (
219+
(reportArgs[1].type === 'Literal' && typeof reportArgs[1].value === 'string') ||
220+
reportArgs[1].type === 'TemplateLiteral'
221+
) {
222+
keys = ['node', 'message', 'data', 'fix'];
223+
} else if (
224+
reportArgs[1].type === 'ObjectExpression' ||
225+
reportArgs[1].type === 'ArrayExpression' ||
226+
(reportArgs[1].type === 'Literal' && typeof reportArgs[1].value !== 'string')
227+
) {
228+
keys = ['node', 'loc', 'message', 'data', 'fix'];
229+
} else {
230+
// Otherwise, we can't statically determine what argument means what, so no safe fix is possible.
231+
return null;
232+
}
233+
234+
return keys
235+
.slice(0, reportArgs.length)
236+
.reduce((reportInfo, key, index) => Object.assign(reportInfo, { [key]: reportArgs[index] }), {});
237+
},
187238
};

Diff for: tests/lib/utils.js

+33
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,37 @@ describe('utils', () => {
287287
});
288288
});
289289
});
290+
291+
describe('getReportInfo', () => {
292+
const CASES = new Map([
293+
[[], () => null],
294+
[['foo', 'bar'], () => null],
295+
[['foo', '"bar"', 'baz', 'qux', 'boop'], args => ({ node: args[0], message: args[1], data: args[2], fix: args[3] })],
296+
[['foo', '`bar`', 'baz', 'qux', 'boop'], args => ({ node: args[0], message: args[1], data: args[2], fix: args[3] })],
297+
[
298+
['foo', '{ bar: 1 }', 'baz', 'qux', 'boop'],
299+
args => ({ node: args[0], loc: args[1], message: args[2], data: args[3], fix: args[4] }),
300+
],
301+
[['foo', 'bar', 'baz'], () => null],
302+
[
303+
['{ node, message }'],
304+
() => ({
305+
node: { type: 'Identifier', name: 'node', start: 17, end: 21 },
306+
message: { type: 'Identifier', name: 'message', start: 23, end: 30 },
307+
}),
308+
],
309+
]);
310+
311+
for (const args of CASES.keys()) {
312+
it(args.join(', '), () => {
313+
const parsedArgs = espree.parse(
314+
`context.report(${args.join(', ')})`,
315+
{ ecmaVersion: 6, loc: false, range: false }
316+
).body[0].expression.arguments;
317+
const reportInfo = utils.getReportInfo(parsedArgs);
318+
319+
assert.deepEqual(reportInfo, CASES.get(args)(parsedArgs));
320+
});
321+
}
322+
});
290323
});

0 commit comments

Comments
 (0)