Skip to content

Commit aabcf1d

Browse files
authored
template-indent: Support member expression paths in tags and functions (#2346)
1 parent e59d9ee commit aabcf1d

7 files changed

+75
-29
lines changed

docs/rules/template-indent.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Under the hood, [strip-indent](https://npmjs.com/package/strip-indent) is used t
6969

7070
## Options
7171

72-
The rule accepts lists of `tags`, `functions`, `selectors` and `comments` to match template literals. `tags` are tagged template literal identifiers, functions are names of utility functions like `stripIndent`, selectors can be any [ESLint selector](https://eslint.org/docs/developer-guide/selectors), and comments are `/* block-commented */` strings.
72+
The rule accepts lists of `tags`, `functions`, `selectors` and `comments` to match template literals. `tags` are tagged template literal identifiers or member expression paths, functions are names or member expression paths of utility functions like `stripIndent`, selectors can be any [ESLint selector](https://eslint.org/docs/developer-guide/selectors), and comments are `/* block-commented */` strings.
7373

7474
Default configuration:
7575

rules/ast/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = {
3333
isNewExpression,
3434
isReferenceIdentifier: require('./is-reference-identifier.js'),
3535
isStaticRequire: require('./is-static-require.js'),
36+
isTaggedTemplateLiteral: require('./is-tagged-template-literal.js'),
3637
isUndefined: require('./is-undefined.js'),
3738

3839
functionTypes: require('./function-types.js'),
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const {isNodeMatches} = require('../utils/is-node-matches.js');
4+
5+
/**
6+
Check if the given node is a tagged template literal.
7+
8+
@param {Node} node - The AST node to check.
9+
@param {string[]} tags - The object name or key paths.
10+
@returns {boolean}
11+
*/
12+
function isTaggedTemplateLiteral(node, tags) {
13+
if (
14+
node.type !== 'TemplateLiteral'
15+
|| node.parent.type !== 'TaggedTemplateExpression'
16+
|| node.parent.quasi !== node
17+
) {
18+
return false;
19+
}
20+
21+
if (tags) {
22+
return isNodeMatches(node.parent.tag, tags);
23+
}
24+
25+
return true;
26+
}
27+
28+
module.exports = isTaggedTemplateLiteral;

rules/escape-case.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'use strict';
22
const {replaceTemplateElement} = require('./fix/index.js');
3-
const {isRegexLiteral, isStringLiteral} = require('./ast/index.js');
4-
const {isNodeMatches} = require('./utils/index.js');
3+
const {
4+
isRegexLiteral,
5+
isStringLiteral,
6+
isTaggedTemplateLiteral,
7+
} = require('./ast/index.js');
58

69
const MESSAGE_ID = 'escape-case';
710
const messages = {
@@ -44,12 +47,7 @@ const create = context => {
4447
});
4548

4649
context.on('TemplateElement', node => {
47-
const templateLiteral = node.parent;
48-
if (
49-
templateLiteral.parent.type === 'TaggedTemplateExpression'
50-
&& templateLiteral.parent.quasi === templateLiteral
51-
&& isNodeMatches(templateLiteral.parent.tag, ['String.raw'])
52-
) {
50+
if (isTaggedTemplateLiteral(node.parent, ['String.raw'])) {
5351
return;
5452
}
5553

rules/no-hex-escape.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'use strict';
22
const {replaceTemplateElement} = require('./fix/index.js');
3-
const {isStringLiteral, isRegexLiteral} = require('./ast/index.js');
4-
const {isNodeMatches} = require('./utils/index.js');
3+
const {
4+
isStringLiteral,
5+
isRegexLiteral,
6+
isTaggedTemplateLiteral,
7+
} = require('./ast/index.js');
58

69
const MESSAGE_ID = 'no-hex-escape';
710
const messages = {
@@ -31,12 +34,7 @@ const create = context => ({
3134
}
3235
},
3336
TemplateElement(node) {
34-
const templateLiteral = node.parent;
35-
if (
36-
templateLiteral.parent.type === 'TaggedTemplateExpression'
37-
&& templateLiteral.parent.quasi === templateLiteral
38-
&& isNodeMatches(templateLiteral.parent.tag, ['String.raw'])
39-
) {
37+
if (isTaggedTemplateLiteral(node.parent, ['String.raw'])) {
4038
return;
4139
}
4240

rules/template-indent.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ const stripIndent = require('strip-indent');
33
const indentString = require('indent-string');
44
const esquery = require('esquery');
55
const {replaceTemplateElement} = require('./fix/index.js');
6-
const {isMethodCall, isCallExpression} = require('./ast/index.js');
6+
const {
7+
isMethodCall,
8+
isCallExpression,
9+
isTaggedTemplateLiteral,
10+
} = require('./ast/index.js');
11+
const {isNodeMatches} = require('./utils/index.js');
712

813
const MESSAGE_ID_IMPROPERLY_INDENTED_TEMPLATE = 'template-indent';
914
const messages = {
@@ -114,10 +119,7 @@ const create = context => {
114119

115120
if (
116121
options.tags.length > 0
117-
&& node.parent.type === 'TaggedTemplateExpression'
118-
&& node.parent.quasi === node
119-
&& node.parent.tag.type === 'Identifier'
120-
&& options.tags.includes(node.parent.tag.name)
122+
&& isTaggedTemplateLiteral(node, options.tags)
121123
) {
122124
return true;
123125
}
@@ -126,8 +128,7 @@ const create = context => {
126128
options.functions.length > 0
127129
&& node.parent.type === 'CallExpression'
128130
&& node.parent.arguments.includes(node)
129-
&& node.parent.callee.type === 'Identifier'
130-
&& options.functions.includes(node.parent.callee.name)
131+
&& isNodeMatches(node.parent.callee, options.functions)
131132
) {
132133
return true;
133134
}

test/template-indent.mjs

+25-5
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ test({
8585
••••••••\`
8686
`),
8787
},
88+
{
89+
options: [{
90+
tags: ['utils.dedent'],
91+
}],
92+
code: fixInput(`
93+
foo = utils.dedent\`
94+
••••••••one
95+
••••••••two
96+
••••••••••three
97+
••••••••\`
98+
`),
99+
errors,
100+
output: fixInput(`
101+
foo = utils.dedent\`
102+
••one
103+
••two
104+
••••three
105+
\`
106+
`),
107+
},
88108
{
89109
options: [{
90110
tags: ['customIndentableTag'],
@@ -412,28 +432,28 @@ test({
412432
},
413433
{
414434
options: [{
415-
functions: ['customDedentFunction'],
435+
functions: ['customDedentFunction1', 'utils.customDedentFunction2'],
416436
}],
417437
code: fixInput(`
418-
foo = customDedentFunction(\`
438+
foo = customDedentFunction1(\`
419439
••••••••one
420440
••••••••two
421441
••••••••••three
422442
••••••••\`)
423-
foo = customDedentFunction('some-other-arg', \`
443+
foo = utils.customDedentFunction2('some-other-arg', \`
424444
••••••••one
425445
••••••••two
426446
••••••••••three
427447
••••••••\`)
428448
`),
429449
errors: [...errors, ...errors],
430450
output: fixInput(`
431-
foo = customDedentFunction(\`
451+
foo = customDedentFunction1(\`
432452
••one
433453
••two
434454
••••three
435455
\`)
436-
foo = customDedentFunction('some-other-arg', \`
456+
foo = utils.customDedentFunction2('some-other-arg', \`
437457
••one
438458
••two
439459
••••three

0 commit comments

Comments
 (0)