UNPKG

5.58 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getWrappingFixer = void 0;
4const utils_1 = require("@typescript-eslint/utils");
5/**
6 * Wraps node with some code. Adds parenthesis as necessary.
7 * @returns Fixer which adds the specified code and parens if necessary.
8 */
9function getWrappingFixer(params) {
10 const { sourceCode, node, innerNode = node, wrap } = params;
11 const innerNodes = Array.isArray(innerNode) ? innerNode : [innerNode];
12 return (fixer) => {
13 const innerCodes = innerNodes.map(innerNode => {
14 let code = sourceCode.getText(innerNode);
15 // check the inner expression's precedence
16 if (!isStrongPrecedenceNode(innerNode)) {
17 // the code we are adding might have stronger precedence than our wrapped node
18 // let's wrap our node in parens in case it has a weaker precedence than the code we are wrapping it in
19 code = `(${code})`;
20 }
21 return code;
22 });
23 // do the wrapping
24 let code = wrap(...innerCodes);
25 // check the outer expression's precedence
26 if (isWeakPrecedenceParent(node)) {
27 // we wrapped the node in some expression which very likely has a different precedence than original wrapped node
28 // let's wrap the whole expression in parens just in case
29 if (!utils_1.ASTUtils.isParenthesized(node, sourceCode)) {
30 code = `(${code})`;
31 }
32 }
33 // check if we need to insert semicolon
34 if (/^[`([]/.exec(code) && isMissingSemicolonBefore(node, sourceCode)) {
35 code = `;${code}`;
36 }
37 return fixer.replaceText(node, code);
38 };
39}
40exports.getWrappingFixer = getWrappingFixer;
41/**
42 * Check if a node will always have the same precedence if it's parent changes.
43 */
44function isStrongPrecedenceNode(innerNode) {
45 return (innerNode.type === utils_1.AST_NODE_TYPES.Literal ||
46 innerNode.type === utils_1.AST_NODE_TYPES.Identifier ||
47 innerNode.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
48 innerNode.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
49 innerNode.type === utils_1.AST_NODE_TYPES.MemberExpression ||
50 innerNode.type === utils_1.AST_NODE_TYPES.CallExpression ||
51 innerNode.type === utils_1.AST_NODE_TYPES.NewExpression ||
52 innerNode.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression);
53}
54/**
55 * Check if a node's parent could have different precedence if the node changes.
56 */
57function isWeakPrecedenceParent(node) {
58 const parent = node.parent;
59 if (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression ||
60 parent.type === utils_1.AST_NODE_TYPES.UnaryExpression ||
61 parent.type === utils_1.AST_NODE_TYPES.BinaryExpression ||
62 parent.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
63 parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
64 parent.type === utils_1.AST_NODE_TYPES.AwaitExpression) {
65 return true;
66 }
67 if (parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
68 parent.object === node) {
69 return true;
70 }
71 if ((parent.type === utils_1.AST_NODE_TYPES.CallExpression ||
72 parent.type === utils_1.AST_NODE_TYPES.NewExpression) &&
73 parent.callee === node) {
74 return true;
75 }
76 if (parent.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression &&
77 parent.tag === node) {
78 return true;
79 }
80 return false;
81}
82/**
83 * Returns true if a node is at the beginning of expression statement and the statement above doesn't end with semicolon.
84 * Doesn't check if the node begins with `(`, `[` or `` ` ``.
85 */
86function isMissingSemicolonBefore(node, sourceCode) {
87 for (;;) {
88 const parent = node.parent;
89 if (parent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
90 const block = parent.parent;
91 if (block.type === utils_1.AST_NODE_TYPES.Program ||
92 block.type === utils_1.AST_NODE_TYPES.BlockStatement) {
93 // parent is an expression statement in a block
94 const statementIndex = block.body.indexOf(parent);
95 const previousStatement = block.body[statementIndex - 1];
96 if (statementIndex > 0 &&
97 sourceCode.getLastToken(previousStatement).value !== ';') {
98 return true;
99 }
100 }
101 }
102 if (!isLeftHandSide(node)) {
103 return false;
104 }
105 node = parent;
106 }
107}
108/**
109 * Checks if a node is LHS of an operator.
110 */
111function isLeftHandSide(node) {
112 const parent = node.parent;
113 // a++
114 if (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression) {
115 return true;
116 }
117 // a + b
118 if ((parent.type === utils_1.AST_NODE_TYPES.BinaryExpression ||
119 parent.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
120 parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression) &&
121 node === parent.left) {
122 return true;
123 }
124 // a ? b : c
125 if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
126 node === parent.test) {
127 return true;
128 }
129 // a(b)
130 if (parent.type === utils_1.AST_NODE_TYPES.CallExpression && node === parent.callee) {
131 return true;
132 }
133 // a`b`
134 if (parent.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression &&
135 node === parent.tag) {
136 return true;
137 }
138 return false;
139}
140//# sourceMappingURL=getWrappingFixer.js.map
\No newline at end of file