Skip to content

Commit

Permalink
template-indent: Support member expression paths in tags and `fun…
Browse files Browse the repository at this point in the history
…ctions` (#2346)
  • Loading branch information
fisker committed May 9, 2024
1 parent e59d9ee commit aabcf1d
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 29 deletions.
2 changes: 1 addition & 1 deletion docs/rules/template-indent.md
Expand Up @@ -69,7 +69,7 @@ Under the hood, [strip-indent](https://npmjs.com/package/strip-indent) is used t

## Options

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.
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.

Default configuration:

Expand Down
1 change: 1 addition & 0 deletions rules/ast/index.js
Expand Up @@ -33,6 +33,7 @@ module.exports = {
isNewExpression,
isReferenceIdentifier: require('./is-reference-identifier.js'),
isStaticRequire: require('./is-static-require.js'),
isTaggedTemplateLiteral: require('./is-tagged-template-literal.js'),
isUndefined: require('./is-undefined.js'),

functionTypes: require('./function-types.js'),
Expand Down
28 changes: 28 additions & 0 deletions rules/ast/is-tagged-template-literal.js
@@ -0,0 +1,28 @@
'use strict';

const {isNodeMatches} = require('../utils/is-node-matches.js');

/**
Check if the given node is a tagged template literal.
@param {Node} node - The AST node to check.
@param {string[]} tags - The object name or key paths.
@returns {boolean}
*/
function isTaggedTemplateLiteral(node, tags) {
if (
node.type !== 'TemplateLiteral'
|| node.parent.type !== 'TaggedTemplateExpression'
|| node.parent.quasi !== node
) {
return false;
}

if (tags) {
return isNodeMatches(node.parent.tag, tags);
}

return true;
}

module.exports = isTaggedTemplateLiteral;
14 changes: 6 additions & 8 deletions rules/escape-case.js
@@ -1,7 +1,10 @@
'use strict';
const {replaceTemplateElement} = require('./fix/index.js');
const {isRegexLiteral, isStringLiteral} = require('./ast/index.js');
const {isNodeMatches} = require('./utils/index.js');
const {
isRegexLiteral,
isStringLiteral,
isTaggedTemplateLiteral,
} = require('./ast/index.js');

const MESSAGE_ID = 'escape-case';
const messages = {
Expand Down Expand Up @@ -44,12 +47,7 @@ const create = context => {
});

context.on('TemplateElement', node => {
const templateLiteral = node.parent;
if (
templateLiteral.parent.type === 'TaggedTemplateExpression'
&& templateLiteral.parent.quasi === templateLiteral
&& isNodeMatches(templateLiteral.parent.tag, ['String.raw'])
) {
if (isTaggedTemplateLiteral(node.parent, ['String.raw'])) {
return;
}

Expand Down
14 changes: 6 additions & 8 deletions rules/no-hex-escape.js
@@ -1,7 +1,10 @@
'use strict';
const {replaceTemplateElement} = require('./fix/index.js');
const {isStringLiteral, isRegexLiteral} = require('./ast/index.js');
const {isNodeMatches} = require('./utils/index.js');
const {
isStringLiteral,
isRegexLiteral,
isTaggedTemplateLiteral,
} = require('./ast/index.js');

const MESSAGE_ID = 'no-hex-escape';
const messages = {
Expand Down Expand Up @@ -31,12 +34,7 @@ const create = context => ({
}
},
TemplateElement(node) {
const templateLiteral = node.parent;
if (
templateLiteral.parent.type === 'TaggedTemplateExpression'
&& templateLiteral.parent.quasi === templateLiteral
&& isNodeMatches(templateLiteral.parent.tag, ['String.raw'])
) {
if (isTaggedTemplateLiteral(node.parent, ['String.raw'])) {
return;
}

Expand Down
15 changes: 8 additions & 7 deletions rules/template-indent.js
Expand Up @@ -3,7 +3,12 @@ const stripIndent = require('strip-indent');
const indentString = require('indent-string');
const esquery = require('esquery');
const {replaceTemplateElement} = require('./fix/index.js');
const {isMethodCall, isCallExpression} = require('./ast/index.js');
const {
isMethodCall,
isCallExpression,
isTaggedTemplateLiteral,
} = require('./ast/index.js');
const {isNodeMatches} = require('./utils/index.js');

const MESSAGE_ID_IMPROPERLY_INDENTED_TEMPLATE = 'template-indent';
const messages = {
Expand Down Expand Up @@ -114,10 +119,7 @@ const create = context => {

if (
options.tags.length > 0
&& node.parent.type === 'TaggedTemplateExpression'
&& node.parent.quasi === node
&& node.parent.tag.type === 'Identifier'
&& options.tags.includes(node.parent.tag.name)
&& isTaggedTemplateLiteral(node, options.tags)
) {
return true;
}
Expand All @@ -126,8 +128,7 @@ const create = context => {
options.functions.length > 0
&& node.parent.type === 'CallExpression'
&& node.parent.arguments.includes(node)
&& node.parent.callee.type === 'Identifier'
&& options.functions.includes(node.parent.callee.name)
&& isNodeMatches(node.parent.callee, options.functions)
) {
return true;
}
Expand Down
30 changes: 25 additions & 5 deletions test/template-indent.mjs
Expand Up @@ -85,6 +85,26 @@ test({
••••••••\`
`),
},
{
options: [{
tags: ['utils.dedent'],
}],
code: fixInput(`
foo = utils.dedent\`
••••••••one
••••••••two
••••••••••three
••••••••\`
`),
errors,
output: fixInput(`
foo = utils.dedent\`
••one
••two
••••three
\`
`),
},
{
options: [{
tags: ['customIndentableTag'],
Expand Down Expand Up @@ -412,28 +432,28 @@ test({
},
{
options: [{
functions: ['customDedentFunction'],
functions: ['customDedentFunction1', 'utils.customDedentFunction2'],
}],
code: fixInput(`
foo = customDedentFunction(\`
foo = customDedentFunction1(\`
••••••••one
••••••••two
••••••••••three
••••••••\`)
foo = customDedentFunction('some-other-arg', \`
foo = utils.customDedentFunction2('some-other-arg', \`
••••••••one
••••••••two
••••••••••three
••••••••\`)
`),
errors: [...errors, ...errors],
output: fixInput(`
foo = customDedentFunction(\`
foo = customDedentFunction1(\`
••one
••two
••••three
\`)
foo = customDedentFunction('some-other-arg', \`
foo = utils.customDedentFunction2('some-other-arg', \`
••one
••two
••••three
Expand Down

0 comments on commit aabcf1d

Please sign in to comment.