11// Copyright 2020 The Chromium Authors. All rights reserved.
22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
4- 'use strict' ;
54
6- /**
7- * @type {import('eslint').Rule.RuleModule }
8- */
9- module . exports = {
5+ import type { TSESTree } from '@typescript-eslint/utils' ;
6+
7+ import { createRule } from './tsUtils.ts' ;
8+
9+ export default createRule ( {
10+ name : 'lit-template-result-or-nothing' ,
1011 meta : {
1112 type : 'problem' ,
12-
1313 docs : {
14- description :
15- 'Enforce use of Lit.LitTemplate type rather than union with Lit.nothing' ,
14+ description : 'Enforce use of Lit.LitTemplate type rather than union with Lit.nothing or {}' ,
1615 category : 'Possible Errors' ,
1716 } ,
1817 fixable : 'code' ,
1918 messages : {
20- useLitTemplateOverEmptyObject :
21- 'Prefer Lit.LitTemplate type over a union with {}' ,
22- useLitTemplateOverTypeOfNothing :
23- 'Prefer Lit.LitTemplate type over a union with Lit.nothing' ,
19+ useLitTemplateOverEmptyObject : 'Prefer Lit.LitTemplate type over a union with {}' ,
20+ useLitTemplateOverTypeOfNothing : 'Prefer Lit.LitTemplate type over a union with typeof Lit.nothing' ,
2421 } ,
25- schema : [ ] , // no options
22+ schema : [ ] , // no options
2623 } ,
27- create : function ( context ) {
28- const sourceCode = context . sourceCode ?? context . getSourceCode ( ) ;
24+ defaultOptions : [ ] ,
25+ create : function ( context ) {
26+ const sourceCode = context . sourceCode ;
2927 const UNION_TYPE_FOR_LIT_TEMPLATE = 'Lit.LitTemplate' ;
3028
31- function checkUnionReturnTypeForLit ( node ) {
29+ function checkUnionReturnTypeForLit ( node : TSESTree . TSUnionType ) : void {
3230 // We want to go through the types in the union and match if:
3331 // 1. We find `Lit.TemplateResult` and `{}`
34- // 2. We find `Lit.TemplateResult` and `Lit.nothing`.
32+ // 2. We find `Lit.TemplateResult` and `typeof Lit.nothing`.
3533 // Otherwise, this node is OK.
3634
37- let templateResultNode = null ;
38- let literalEmptyObjectNode = null ;
39- let litNothingNode = null ;
35+ let templateResultNode : TSESTree . TSTypeReference | null = null ;
36+ let literalEmptyObjectNode : TSESTree . TSTypeLiteral | null = null ;
37+ let litNothingNode : TSESTree . TSTypeQuery | null = null ;
4038
41- const nonLitRelatedNodesInUnion = new Set ( ) ;
39+ const nonLitRelatedNodesInUnion = new Set < TSESTree . TypeNode > ( ) ;
4240
4341 for ( const typeNode of node . types ) {
4442 // This matches a type reference of X.y. Now we see if X === 'Lit' and y === 'TemplateResult'
45- if (
46- typeNode . type === 'TSTypeReference' &&
47- typeNode . typeName . type === 'TSQualifiedName'
48- ) {
43+ if ( typeNode . type === 'TSTypeReference' && typeNode . typeName . type === 'TSQualifiedName' &&
44+ typeNode . typeName . left . type === 'Identifier' ) {
4945 const leftText = typeNode . typeName . left . name ;
5046 const rightText = typeNode . typeName . right . name ;
5147 if ( leftText === 'Lit' && rightText === 'TemplateResult' ) {
@@ -57,9 +53,8 @@ module.exports = {
5753 literalEmptyObjectNode = typeNode ;
5854 continue ;
5955 } else if (
60- typeNode . type === 'TSTypeQuery' &&
61- typeNode . exprName . type === 'TSQualifiedName'
62- ) {
56+ typeNode . type === 'TSTypeQuery' && typeNode . exprName . type === 'TSQualifiedName' &&
57+ typeNode . exprName . left . type === 'Identifier' ) {
6358 // matches `typeof X.y`
6459 const leftText = typeNode . exprName . left . name ;
6560 const rightText = typeNode . exprName . right . name ;
@@ -90,10 +85,10 @@ module.exports = {
9085 // So we capture all the non-lit related types in the union, and get
9186 // their text content, so we can keep them around when we run the fixer.
9287 const nonLitRelatedTypesToKeep = Array . from (
93- nonLitRelatedNodesInUnion ,
94- node => {
95- return sourceCode . getText ( node ) ;
96- } ,
88+ nonLitRelatedNodesInUnion ,
89+ node => {
90+ return sourceCode . getText ( node ) ;
91+ } ,
9792 ) ;
9893 const newText = [
9994 UNION_TYPE_FOR_LIT_TEMPLATE ,
@@ -102,48 +97,43 @@ module.exports = {
10297
10398 context . report ( {
10499 node,
105- messageId : litNothingNode
106- ? 'useLitTemplateOverTypeOfNothing'
107- : 'useLitTemplateOverEmptyObject' ,
100+ messageId : litNothingNode ? 'useLitTemplateOverTypeOfNothing' : 'useLitTemplateOverEmptyObject' ,
108101 fix ( fixer ) {
109102 return fixer . replaceText ( node , newText ) ;
110103 } ,
111104 } ) ;
112105 }
113106
114- function checkTSTypeAnnotationForPotentialIssue ( node ) {
107+ function checkTSTypeAnnotationForPotentialIssue ( node : TSESTree . TSTypeAnnotation ) : void {
115108 const annotation = node . typeAnnotation ;
116- if ( ! annotation ) {
117- return ;
118- }
109+
119110 if ( annotation . type === 'TSUnionType' ) {
120111 // matches foo(): X|Y
121112 checkUnionReturnTypeForLit ( annotation ) ;
122113 } else if ( annotation . type === 'TSTypeReference' ) {
123114 // matches many things, including foo(): Promise<X|Y>, which we do want
124115 // to check.
125116
126- if ( annotation . typeName . name !== 'Promise' ) {
117+ if ( annotation . typeName . type !== 'Identifier' || annotation . typeName . name !== 'Promise' ) {
127118 // If it's not a promise, bail out.
128119 return ;
129120 }
121+
130122 // Represents the generic type passed to the promise: if our code is
131123 // Promise<X>, this node represents the X.
132- const promiseGenericNode = annotation . typeArguments . params [ 0 ] ;
124+ const promiseGenericNode = annotation . typeArguments ? .params [ 0 ] ;
133125 if ( promiseGenericNode && promiseGenericNode . type === 'TSUnionType' ) {
134126 checkUnionReturnTypeForLit ( promiseGenericNode ) ;
135127 }
136128 }
137129 }
138130
139- function checkFunctionDeclarationOrExpressionForUnionType ( node ) {
131+ function checkFunctionDeclarationOrExpressionForUnionType (
132+ node : TSESTree . FunctionDeclaration | TSESTree . FunctionExpression | TSESTree . ArrowFunctionExpression ) : void {
140133 if ( ! node . returnType ) {
141134 return ;
142135 }
143136
144- if ( node . returnType . type !== 'TSTypeAnnotation' ) {
145- return ;
146- }
147137 checkTSTypeAnnotationForPotentialIssue ( node . returnType ) ;
148138 }
149139
@@ -165,4 +155,4 @@ module.exports = {
165155 } ,
166156 } ;
167157 } ,
168- } ;
158+ } ) ;
0 commit comments