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