diff --git a/lib/rules/custom-event-name-casing.js b/lib/rules/custom-event-name-casing.js index 967cd6789..6c5f072c3 100644 --- a/lib/rules/custom-event-name-casing.js +++ b/lib/rules/custom-event-name-casing.js @@ -48,7 +48,7 @@ function getNameParamNode(node) { * @param {CallExpression} node CallExpression */ function getCalleeMemberNode(node) { - const callee = utils.unwrapChainExpression(node.callee) + const callee = utils.skipChainExpression(node.callee) if (callee.type === 'MemberExpression') { const name = utils.getStaticPropertyName(callee) @@ -116,7 +116,7 @@ module.exports = { utils.compositingVisitors( utils.defineVueVisitor(context, { onSetupFunctionEnter(node, { node: vueNode }) { - const contextParam = utils.unwrapAssignmentPattern(node.params[1]) + const contextParam = utils.skipDefaultParamValue(node.params[1]) if (!contextParam) { // no arguments return diff --git a/lib/rules/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js index 064790aa6..9812d61c0 100644 --- a/lib/rules/no-async-in-computed-properties.js +++ b/lib/rules/no-async-in-computed-properties.js @@ -26,7 +26,7 @@ const TIMED_FUNCTIONS = [ * @param {CallExpression} node */ function isTimedFunction(node) { - const callee = utils.unwrapChainExpression(node.callee) + const callee = utils.skipChainExpression(node.callee) return ( ((node.type === 'CallExpression' && callee.type === 'Identifier' && @@ -45,7 +45,7 @@ function isTimedFunction(node) { * @param {CallExpression} node */ function isPromise(node) { - const callee = utils.unwrapChainExpression(node.callee) + const callee = utils.skipChainExpression(node.callee) if (node.type === 'CallExpression' && callee.type === 'MemberExpression') { return ( // hello.PROMISE_FUNCTION() diff --git a/lib/rules/no-deprecated-events-api.js b/lib/rules/no-deprecated-events-api.js index 608d306d4..f393f59ef 100644 --- a/lib/rules/no-deprecated-events-api.js +++ b/lib/rules/no-deprecated-events-api.js @@ -48,7 +48,7 @@ module.exports = { } if ( - utils.unwrapChainExpression(call.callee) !== node || + utils.skipChainExpression(call.callee) !== node || !['$on', '$off', '$once'].includes( utils.getStaticPropertyName(node) || '' ) diff --git a/lib/rules/no-deprecated-vue-config-keycodes.js b/lib/rules/no-deprecated-vue-config-keycodes.js index 53effb5d4..4db268e44 100644 --- a/lib/rules/no-deprecated-vue-config-keycodes.js +++ b/lib/rules/no-deprecated-vue-config-keycodes.js @@ -33,7 +33,7 @@ module.exports = { "MemberExpression[property.type='Identifier'][property.name='keyCodes']"( node ) { - const config = utils.unwrapChainExpression(node.object) + const config = utils.skipChainExpression(node.object) if ( config.type !== 'MemberExpression' || config.property.type !== 'Identifier' || diff --git a/lib/rules/no-multiple-slot-args.js b/lib/rules/no-multiple-slot-args.js index 90b6755b9..474a56ba2 100644 --- a/lib/rules/no-multiple-slot-args.js +++ b/lib/rules/no-multiple-slot-args.js @@ -100,7 +100,7 @@ module.exports = { return utils.defineVueVisitor(context, { /** @param {MemberExpression} node */ MemberExpression(node) { - const object = utils.unwrapChainExpression(node.object) + const object = utils.skipChainExpression(node.object) if (object.type !== 'MemberExpression') { return } diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js index 95acdbe23..e77f63c4d 100644 --- a/lib/rules/no-setup-props-destructure.js +++ b/lib/rules/no-setup-props-destructure.js @@ -49,7 +49,7 @@ module.exports = { return } - const rightNode = utils.unwrapChainExpression(right) + const rightNode = utils.skipChainExpression(right) if ( left.type !== 'ArrayPattern' && left.type !== 'ObjectPattern' && @@ -60,7 +60,7 @@ module.exports = { /** @type {Expression | Super} */ let rightId = rightNode while (rightId.type === 'MemberExpression') { - rightId = utils.unwrapChainExpression(rightId.object) + rightId = utils.skipChainExpression(rightId.object) } if (rightId.type === 'Identifier' && propsReferenceIds.has(rightId)) { report(left, 'getProperty') @@ -84,7 +84,7 @@ module.exports = { } }, onSetupFunctionEnter(node) { - const propsParam = utils.unwrapAssignmentPattern(node.params[0]) + const propsParam = utils.skipDefaultParamValue(node.params[0]) if (!propsParam) { // no arguments return diff --git a/lib/rules/require-default-prop.js b/lib/rules/require-default-prop.js index 54125fe98..9e9f1026f 100644 --- a/lib/rules/require-default-prop.js +++ b/lib/rules/require-default-prop.js @@ -123,7 +123,7 @@ module.exports = { * @return {Boolean} */ function isBooleanProp(prop) { - const value = utils.unwrapTypes(prop.value) + const value = utils.skipTSAsExpression(prop.value) return ( isValueNodeOfBooleanType(value) || diff --git a/lib/rules/require-explicit-emits.js b/lib/rules/require-explicit-emits.js index 903210c24..27ae600fa 100644 --- a/lib/rules/require-explicit-emits.js +++ b/lib/rules/require-explicit-emits.js @@ -261,7 +261,7 @@ module.exports = { * @param {VueObjectData} data */ 'CallExpression[arguments.0.type=Literal]'(node, { node: vueNode }) { - const callee = utils.unwrapChainExpression(node.callee) + const callee = utils.skipChainExpression(node.callee) const nameLiteralNode = node.arguments[0] if (!nameLiteralNode || typeof nameLiteralNode.value !== 'string') { // cannot check @@ -288,7 +288,7 @@ module.exports = { // verify setup(props,{emit}) {emit()} verify(emitsDeclarations, nameLiteralNode, vueNode) } else if (emit && emit.name === 'emit') { - const memObject = utils.unwrapChainExpression(emit.member.object) + const memObject = utils.skipChainExpression(emit.member.object) if ( memObject.type === 'Identifier' && contextReferenceIds.has(memObject) @@ -301,7 +301,7 @@ module.exports = { // verify $emit if (emit && emit.name === '$emit') { - const memObject = utils.unwrapChainExpression(emit.member.object) + const memObject = utils.skipChainExpression(emit.member.object) if (utils.isThis(memObject, context)) { // verify this.$emit() verify(emitsDeclarations, nameLiteralNode, vueNode) diff --git a/lib/rules/require-slots-as-functions.js b/lib/rules/require-slots-as-functions.js index 2a3180f4e..658071d22 100644 --- a/lib/rules/require-slots-as-functions.js +++ b/lib/rules/require-slots-as-functions.js @@ -103,7 +103,7 @@ module.exports = { return utils.defineVueVisitor(context, { /** @param {MemberExpression} node */ MemberExpression(node) { - const object = utils.unwrapChainExpression(node.object) + const object = utils.skipChainExpression(node.object) if (object.type !== 'MemberExpression') { return } diff --git a/lib/rules/require-valid-default-prop.js b/lib/rules/require-valid-default-prop.js index 1a2ab4899..5f454e2a5 100644 --- a/lib/rules/require-valid-default-prop.js +++ b/lib/rules/require-valid-default-prop.js @@ -126,7 +126,7 @@ module.exports = { * @returns { StandardValueType | FunctionExprValueType | FunctionValueType | null } */ function getValueType(targetNode) { - const node = utils.unwrapChainExpression(targetNode) + const node = utils.skipChainExpression(targetNode) if (node.type === 'CallExpression') { // Symbol(), Number() ... if ( diff --git a/lib/utils/index.js b/lib/utils/index.js index ce5e7b475..e644b6228 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -681,7 +681,7 @@ module.exports = { type: 'object', key: prop.key, propName, - value: unwrapTypes(prop.value), + value: skipTSAsExpression(prop.value), node: prop } } @@ -689,7 +689,7 @@ module.exports = { type: 'object', key: null, propName: null, - value: unwrapTypes(prop.value), + value: skipTSAsExpression(prop.value), node: prop } }) @@ -754,7 +754,7 @@ module.exports = { type: 'object', key: prop.key, emitName, - value: unwrapTypes(prop.value), + value: skipTSAsExpression(prop.value), node: prop } } @@ -762,7 +762,7 @@ module.exports = { type: 'object', key: null, emitName: null, - value: unwrapTypes(prop.value), + value: skipTSAsExpression(prop.value), node: prop } }) @@ -823,7 +823,7 @@ module.exports = { .map((cp) => { const key = getStaticPropertyName(cp) /** @type {Expression} */ - const propValue = unwrapTypes(cp.value) + const propValue = skipTSAsExpression(cp.value) /** @type {BlockStatement | null} */ let value = null @@ -1017,7 +1017,7 @@ module.exports = { const callee = callExpr.callee if (callee.type === 'MemberExpression') { - const calleeObject = unwrapTypes(callee.object) + const calleeObject = skipTSAsExpression(callee.object) if ( calleeObject.type === 'Identifier' && @@ -1271,11 +1271,11 @@ module.exports = { getMemberChaining(node) { /** @type {MemberExpression[]} */ const nodes = [] - let n = unwrapChainExpression(node) + let n = skipChainExpression(node) while (n.type === 'MemberExpression') { nodes.push(n) - n = unwrapChainExpression(n.object) + n = skipChainExpression(n.object) } return [n, ...nodes.reverse()] @@ -1339,24 +1339,17 @@ module.exports = { */ isPropertyChain, /** - * Unwrap typescript types like "X as F" - * @template T - * @param {T} node - * @return {T} + * Retrieve `TSAsExpression#expression` value if the given node a `TSAsExpression` node. Otherwise, pass through it. */ - unwrapTypes, + skipTSAsExpression, /** - * Unwrap AssignmentPattern like "(a = 1) => ret" - * @param { AssignmentPattern | RestElement | ArrayPattern | ObjectPattern | Identifier } node - * @return { RestElement | ArrayPattern | ObjectPattern | Identifier} + * Retrieve `AssignmentPattern#left` value if the given node a `AssignmentPattern` node. Otherwise, pass through it. */ - unwrapAssignmentPattern, + skipDefaultParamValue, /** - * Unwrap ChainExpression like "(a?.b)" - * @param { Expression | Super } node - * @return { Expression | Super } + * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it. */ - unwrapChainExpression, + skipChainExpression, /** * Check whether the given node is `this` or variable that stores `this`. @@ -1617,20 +1610,21 @@ function isVElement(node) { } /** - * Unwrap typescript types like "X as F" - * @template T - * @param {T} node - * @return {T} + * Retrieve `TSAsExpression#expression` value if the given node a `TSAsExpression` node. Otherwise, pass through it. + * @template T Node type + * @param {T | TSAsExpression} node The node to address. + * @returns {T} The `TSAsExpression#expression` value if the node is a `TSAsExpression` node. Otherwise, the node. */ -function unwrapTypes(node) { +function skipTSAsExpression(node) { if (!node) { return node } // @ts-expect-error if (node.type === 'TSAsExpression') { // @ts-expect-error - return unwrapTypes(node.expression) + return skipTSAsExpression(node.expression) } + // @ts-expect-error return node } @@ -1661,36 +1655,40 @@ function isPropertyChain(prop, node) { } /** - * Unwrap AssignmentPattern like "(a = 1) => ret" - * @param { AssignmentPattern | RestElement | ArrayPattern | ObjectPattern | Identifier } node - * @return { RestElement | ArrayPattern | ObjectPattern | Identifier } + * Retrieve `AssignmentPattern#left` value if the given node a `AssignmentPattern` node. Otherwise, pass through it. + * @template T Node type + * @param {T | AssignmentPattern} node The node to address. + * @return {T} The `AssignmentPattern#left` value if the node is a `AssignmentPattern` node. Otherwise, the node. */ -function unwrapAssignmentPattern(node) { +function skipDefaultParamValue(node) { if (!node) { return node } + // @ts-expect-error if (node.type === 'AssignmentPattern') { // @ts-expect-error - return unwrapAssignmentPattern(node.left) + return skipDefaultParamValue(node.left) } + // @ts-expect-error return node } /** - * Unwrap ChainExpression like "(a?.b)" - * @template T - * @param {T} node - * @return {T} + * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it. + * @template T Node type + * @param {T | ChainExpression} node The node to address. + * @returns {T} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node. */ -function unwrapChainExpression(node) { +function skipChainExpression(node) { if (!node) { return node } // @ts-expect-error if (node.type === 'ChainExpression') { // @ts-expect-error - return unwrapChainExpression(node.expression) + return skipChainExpression(node.expression) } + // @ts-expect-error return node } @@ -1792,7 +1790,7 @@ function isVueComponent(node) { const callee = node.callee if (callee.type === 'MemberExpression') { - const calleeObject = unwrapTypes(callee.object) + const calleeObject = skipTSAsExpression(callee.object) if (calleeObject.type === 'Identifier') { const propName = getStaticPropertyName(callee) @@ -1846,7 +1844,8 @@ function isVueComponent(node) { function isObjectArgument(node) { return ( node.arguments.length > 0 && - unwrapTypes(node.arguments.slice(-1)[0]).type === 'ObjectExpression' + skipTSAsExpression(node.arguments.slice(-1)[0]).type === + 'ObjectExpression' ) } } @@ -1864,7 +1863,7 @@ function isVueInstance(node) { callee.type === 'Identifier' && callee.name === 'Vue' && node.arguments.length && - unwrapTypes(node.arguments[0]).type === 'ObjectExpression' + skipTSAsExpression(node.arguments[0]).type === 'ObjectExpression' ) } @@ -1884,7 +1883,7 @@ function getVueObjectType(context, node) { const filePath = context.getFilename() if ( isVueComponentFile(parent, filePath) && - unwrapTypes(parent.declaration) === node + skipTSAsExpression(parent.declaration) === node ) { return 'export' } @@ -1892,13 +1891,16 @@ function getVueObjectType(context, node) { // Vue.component('xxx', {}) || component('xxx', {}) if ( isVueComponent(parent) && - unwrapTypes(parent.arguments.slice(-1)[0]) === node + skipTSAsExpression(parent.arguments.slice(-1)[0]) === node ) { return 'definition' } } else if (parent.type === 'NewExpression') { // new Vue({}) - if (isVueInstance(parent) && unwrapTypes(parent.arguments[0]) === node) { + if ( + isVueInstance(parent) && + skipTSAsExpression(parent.arguments[0]) === node + ) { return 'instance' } }