1 | 'use strict';
|
2 | const astUtils = require('eslint-ast-utils');
|
3 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
4 | const isLiteralValue = require('./utils/is-literal-value');
|
5 |
|
6 | const isApplySignature = (argument1, argument2) => (
|
7 |
|
8 | (isLiteralValue(argument1, null) ||
|
9 | argument1.type === 'ThisExpression') &&
|
10 | (argument2.type === 'ArrayExpression' ||
|
11 | (argument2.type === 'Identifier' &&
|
12 | argument2.name === 'arguments'))
|
13 | );
|
14 |
|
15 | const getReflectApplyCall = (sourceCode, func, receiver, arguments_) => (
|
16 | `Reflect.apply(${sourceCode.getText(func)}, ${sourceCode.getText(receiver)}, ${sourceCode.getText(arguments_)})`
|
17 | );
|
18 |
|
19 | const fixDirectApplyCall = (node, sourceCode) => {
|
20 | if (
|
21 | astUtils.getPropertyName(node.callee) === 'apply' &&
|
22 | node.arguments.length === 2 &&
|
23 | isApplySignature(node.arguments[0], node.arguments[1])
|
24 | ) {
|
25 | return fixer => (
|
26 | fixer.replaceText(
|
27 | node,
|
28 | getReflectApplyCall(sourceCode, node.callee.object, node.arguments[0], node.arguments[1])
|
29 | )
|
30 | );
|
31 | }
|
32 | };
|
33 |
|
34 | const fixFunctionPrototypeCall = (node, sourceCode) => {
|
35 | if (
|
36 | astUtils.getPropertyName(node.callee) === 'call' &&
|
37 | astUtils.getPropertyName(node.callee.object) === 'apply' &&
|
38 | astUtils.getPropertyName(node.callee.object.object) === 'prototype' &&
|
39 | node.callee.object.object.object &&
|
40 | node.callee.object.object.object.type === 'Identifier' &&
|
41 | node.callee.object.object.object.name === 'Function' &&
|
42 | node.arguments.length === 3 &&
|
43 | isApplySignature(node.arguments[1], node.arguments[2])
|
44 | ) {
|
45 | return fixer => (
|
46 | fixer.replaceText(
|
47 | node,
|
48 | getReflectApplyCall(sourceCode, node.arguments[0], node.arguments[1], node.arguments[2])
|
49 | )
|
50 | );
|
51 | }
|
52 | };
|
53 |
|
54 | const create = context => {
|
55 | return {
|
56 | CallExpression: node => {
|
57 | if (
|
58 | !(
|
59 | node.callee.type === 'MemberExpression' &&
|
60 | !['Literal', 'ArrayExpression', 'ObjectExpression'].includes(node.callee.object.type)
|
61 | )
|
62 | ) {
|
63 | return;
|
64 | }
|
65 |
|
66 | const sourceCode = context.getSourceCode();
|
67 | const fix = fixDirectApplyCall(node, sourceCode) || fixFunctionPrototypeCall(node, sourceCode);
|
68 | if (fix) {
|
69 | context.report({
|
70 | node,
|
71 | message: 'Prefer `Reflect.apply()` over `Function#apply()`.',
|
72 | fix
|
73 | });
|
74 | }
|
75 | }
|
76 | };
|
77 | };
|
78 |
|
79 | module.exports = {
|
80 | create,
|
81 | meta: {
|
82 | type: 'suggestion',
|
83 | docs: {
|
84 | url: getDocumentationUrl(__filename)
|
85 | },
|
86 | fixable: 'code'
|
87 | }
|
88 | };
|