mirror of https://github.com/grafana/grafana
Chore: Add new `no-translation-top-level` eslint rule (#101550)
parent
4f0e76ec56
commit
ac0fd38183
@ -0,0 +1,59 @@ |
||||
// @ts-check |
||||
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils'); |
||||
|
||||
/** |
||||
* @typedef {import('@typescript-eslint/utils').TSESTree.Node} Node |
||||
* @typedef {import('@typescript-eslint/utils').TSESLint.RuleContext<string, unknown[]>} RuleContext |
||||
*/ |
||||
|
||||
const createRule = ESLintUtils.RuleCreator( |
||||
(name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}` |
||||
); |
||||
|
||||
/** |
||||
* @param {RuleContext} context |
||||
* @param {Node} node |
||||
* @returns {boolean} |
||||
*/ |
||||
const isInFunction = (context, node) => { |
||||
const ancestors = context.sourceCode.getAncestors(node); |
||||
return ancestors.some((anc) => { |
||||
return [ |
||||
AST_NODE_TYPES.ArrowFunctionExpression, |
||||
AST_NODE_TYPES.FunctionDeclaration, |
||||
AST_NODE_TYPES.FunctionExpression, |
||||
AST_NODE_TYPES.ClassDeclaration, |
||||
].includes(anc.type); |
||||
}); |
||||
}; |
||||
|
||||
const noTranslationTopLevel = createRule({ |
||||
create(context) { |
||||
return { |
||||
CallExpression(node) { |
||||
if (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 't') { |
||||
if (!isInFunction(context, node)) { |
||||
context.report({ |
||||
node, |
||||
messageId: 'noMethodOutsideComponent', |
||||
}); |
||||
} |
||||
} |
||||
}, |
||||
}; |
||||
}, |
||||
name: 'no-translation-top-level', |
||||
meta: { |
||||
type: 'suggestion', |
||||
docs: { |
||||
description: 'Do not use translation functions outside of components', |
||||
}, |
||||
messages: { |
||||
noMethodOutsideComponent: 'Do not use the t() function outside of a component or function', |
||||
}, |
||||
schema: [], |
||||
}, |
||||
defaultOptions: [], |
||||
}); |
||||
|
||||
module.exports = noTranslationTopLevel; |
@ -0,0 +1,58 @@ |
||||
import { RuleTester } from 'eslint'; |
||||
|
||||
import noTranslationTopLevel from '../rules/no-translation-top-level.cjs'; |
||||
|
||||
RuleTester.setDefaultConfig({ |
||||
languageOptions: { |
||||
ecmaVersion: 2018, |
||||
sourceType: 'module', |
||||
parserOptions: { |
||||
ecmaFeatures: { |
||||
jsx: true, |
||||
}, |
||||
}, |
||||
}, |
||||
}); |
||||
|
||||
const expectedErrorMessage = 'Do not use the t() function outside of a component or function'; |
||||
|
||||
const ruleTester = new RuleTester(); |
||||
|
||||
ruleTester.run('eslint no-translation-top-level', noTranslationTopLevel, { |
||||
valid: [ |
||||
{ |
||||
code: ` |
||||
function Component() { |
||||
return <div>{t('some.key', 'Some text')}</div>; |
||||
} |
||||
`,
|
||||
}, |
||||
{ |
||||
code: `const foo = () => t('some.key', 'Some text');`, |
||||
}, |
||||
{ |
||||
code: `const foo = ttt('some.key', 'Some text');`, |
||||
}, |
||||
{ |
||||
code: `class Component {
|
||||
render() { |
||||
return t('some.key', 'Some text'); |
||||
} |
||||
}`,
|
||||
}, |
||||
], |
||||
invalid: [ |
||||
{ |
||||
code: `const thing = t('some.key', 'Some text');`, |
||||
errors: [{ message: expectedErrorMessage }], |
||||
}, |
||||
{ |
||||
code: `const things = [t('some.key', 'Some text')];`, |
||||
errors: [{ message: expectedErrorMessage }], |
||||
}, |
||||
{ |
||||
code: `const objectThings = [{foo: t('some.key', 'Some text')}];`, |
||||
errors: [{ message: expectedErrorMessage }], |
||||
}, |
||||
], |
||||
}); |
Loading…
Reference in new issue