Skip to content

Commit 2ae5870

Browse files
G-RathSimenB
authored andcommitted
chore(tsutils): refactor StringNode & related functions (#374)
1 parent 9653c2b commit 2ae5870

File tree

3 files changed

+101
-42
lines changed

3 files changed

+101
-42
lines changed

src/rules/no-empty-title.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
import {
2-
createRule,
3-
getStringValue,
4-
hasExpressions,
5-
isDescribe,
6-
isStringNode,
7-
isTemplateLiteral,
8-
isTestCase,
9-
} from './tsUtils';
1+
import { createRule, isDescribe, isStringNode, isTestCase } from './tsUtils';
102

113
export default createRule({
124
name: __filename,
@@ -31,18 +23,14 @@ export default createRule({
3123
return;
3224
}
3325
const [firstArgument] = node.arguments;
34-
if (!isStringNode(firstArgument)) {
26+
if (!firstArgument || !isStringNode(firstArgument, '')) {
3527
return;
3628
}
37-
if (isTemplateLiteral(firstArgument) && hasExpressions(firstArgument)) {
38-
return;
39-
}
40-
if (getStringValue(firstArgument) === '') {
41-
context.report({
42-
messageId: isDescribe(node) ? 'describe' : 'test',
43-
node,
44-
});
45-
}
29+
30+
context.report({
31+
messageId: isDescribe(node) ? 'describe' : 'test',
32+
node,
33+
});
4634
},
4735
};
4836
},

src/rules/no-identical-title.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default createRule({
4646
}
4747
const [firstArgument] = node.arguments;
4848
if (
49+
!firstArgument ||
4950
!isStringNode(firstArgument) ||
5051
(isTemplateLiteral(firstArgument) && hasExpressions(firstArgument))
5152
) {

src/rules/tsUtils.ts

Lines changed: 93 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,99 @@ export const createRule = ESLintUtils.RuleCreator(name => {
1515
return `${REPO_URL}/blob/v${version}/docs/rules/${ruleName}.md`;
1616
});
1717

18+
/**
19+
* A `Literal` with a `value` of type `string`.
20+
*/
21+
export interface StringLiteral<Value extends string = string>
22+
extends TSESTree.Literal {
23+
value: Value;
24+
}
25+
26+
/**
27+
* Checks if the given `node` is a `StringLiteral`.
28+
*
29+
* If a `value` is provided & the `node` is a `StringLiteral`,
30+
* the `value` will be compared to that of the `StringLiteral`.
31+
*
32+
* @param {Node} node
33+
* @param {V?} value
34+
*
35+
* @return {node is StringLiteral<V>}
36+
*
37+
* @template {V}.
38+
*/
39+
const isStringLiteral = <V extends string>(
40+
node: TSESTree.Node,
41+
value?: V,
42+
): node is StringLiteral<V> =>
43+
node.type === AST_NODE_TYPES.Literal &&
44+
typeof node.value === 'string' &&
45+
(value === undefined || node.value === value);
46+
47+
interface TemplateLiteral<Value extends string = string>
48+
extends TSESTree.TemplateLiteral {
49+
quasis: [TSESTree.TemplateElement & { value: { raw: Value; cooked: Value } }];
50+
}
51+
52+
/**
53+
* Checks if the given `node` is a `TemplateLiteral`.
54+
*
55+
* Complex `TemplateLiteral`s are not considered specific, and so will return `false`.
56+
*
57+
* If a `value` is provided & the `node` is a `TemplateLiteral`,
58+
* the `value` will be compared to that of the `TemplateLiteral`.
59+
*
60+
* @param {Node} node
61+
* @param {V?} value
62+
*
63+
* @return {node is TemplateLiteral<V>}
64+
*
65+
* @template V
66+
*/
67+
export const isTemplateLiteral = <V extends string>(
68+
node: TSESTree.Node,
69+
value?: V,
70+
): node is TemplateLiteral<V> =>
71+
node.type === AST_NODE_TYPES.TemplateLiteral &&
72+
(value === undefined ||
73+
(node.quasis.length === 1 && // bail out if not simple
74+
node.quasis[0].value.raw === value));
75+
76+
type StringNode<S extends string = string> =
77+
| StringLiteral<S>
78+
| TemplateLiteral<S>;
79+
80+
/**
81+
* Checks if the given `node` is a {@link StringNode}.
82+
*
83+
* @param {Node} node
84+
* @param {V?} specifics
85+
*
86+
* @return {node is StringNode}
87+
*
88+
* @template V
89+
*/
90+
export const isStringNode = <V extends string>(
91+
node: TSESTree.Node,
92+
specifics?: V,
93+
): node is StringNode<V> =>
94+
isStringLiteral(node, specifics) || isTemplateLiteral(node, specifics);
95+
96+
/**
97+
* Gets the value of the given `StringNode`.
98+
*
99+
* If the `node` is a `TemplateLiteral`, the `raw` value is used;
100+
* otherwise, `value` is returned instead.
101+
*
102+
* @param {StringNode<S>} node
103+
*
104+
* @return {S}
105+
*
106+
* @template S
107+
*/
108+
export const getStringValue = <S extends string>(node: StringNode<S>): S =>
109+
isTemplateLiteral(node) ? node.quasis[0].value.raw : node.value;
110+
18111
interface JestExpectIdentifier extends TSESTree.Identifier {
19112
name: 'expect';
20113
}
@@ -217,34 +310,11 @@ export const isLiteralNode = (node: {
217310
type: AST_NODE_TYPES;
218311
}): node is TSESTree.Literal => node.type === AST_NODE_TYPES.Literal;
219312

220-
export interface StringLiteral extends TSESTree.Literal {
221-
value: string;
222-
}
223-
224-
export type StringNode = StringLiteral | TSESTree.TemplateLiteral;
225-
226-
export const isStringLiteral = (node: TSESTree.Node): node is StringLiteral =>
227-
node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string';
228-
229-
export const isTemplateLiteral = (
230-
node: TSESTree.Node,
231-
): node is TSESTree.TemplateLiteral =>
232-
node && node.type === AST_NODE_TYPES.TemplateLiteral;
233-
234-
export const isStringNode = (
235-
node: TSESTree.Node | undefined,
236-
): node is StringNode =>
237-
node !== undefined && (isStringLiteral(node) || isTemplateLiteral(node));
238-
239313
export const hasExpressions = (
240314
node: TSESTree.Node,
241315
): node is TSESTree.Expression =>
242316
'expressions' in node && node.expressions.length > 0;
243317

244-
/* istanbul ignore next we'll need this later */
245-
export const getStringValue = (arg: StringNode): string =>
246-
isTemplateLiteral(arg) ? arg.quasis[0].value.raw : arg.value;
247-
248318
const collectReferences = (scope: TSESLint.Scope.Scope) => {
249319
const locals = new Set();
250320
const unresolved = new Set();

0 commit comments

Comments
 (0)