UNPKG

eslint-plugin-ember

Version:
91 lines (84 loc) 2.68 kB
/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'suggestion', docs: { description: 'disallow unnecessary usage of (fn) helper', category: 'Best Practices', url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-redundant-fn.md', templateMode: 'both', }, fixable: 'code', schema: [], messages: { redundant: 'Unnecessary use of (fn) helper. Pass the function directly instead: {{suggestion}}', }, originallyFrom: { name: 'ember-template-lint', rule: 'lib/rules/no-redundant-fn.js', docs: 'docs/rule/no-redundant-fn.md', tests: 'test/unit/rules/no-redundant-fn-test.js', }, }, create(context) { const sourceCode = context.sourceCode; /** * Check whether `fn` in the template resolves to a local/imported binding. * If it does, the user has shadowed Ember's built-in `fn` helper and we * should not flag the usage. */ function isFnShadowed(node) { const head = node.path?.head; if (!head) { return false; } const scope = sourceCode.getScope(node); const ref = scope.references.find((r) => r.identifier === head); return ref?.resolved !== null && ref?.resolved !== undefined; } function checkFnUsage(node) { // Check if this is an (fn) call with only one argument (the function itself) if ( node.path && node.path.type === 'GlimmerPathExpression' && node.path.original === 'fn' && node.params && node.params.length === 1 && !node.hash?.pairs?.length ) { // In gjs/gts, `fn` may be shadowed by a local import — skip if so. if (isFnShadowed(node)) { return; } const param = node.params[0]; const paramText = param.type === 'GlimmerPathExpression' ? param.original : context.sourceCode.getText(param); context.report({ node, messageId: 'redundant', data: { suggestion: paramText, }, fix(fixer) { if (node.type === 'GlimmerMustacheStatement') { return fixer.replaceTextRange(node.range, `{{${paramText}}}`); } // GlimmerSubExpression: (fn this.handleClick) → this.handleClick return fixer.replaceTextRange(node.range, paramText); }, }); } } return { GlimmerSubExpression(node) { checkFnUsage(node); }, GlimmerMustacheStatement(node) { checkFnUsage(node); }, }; }, };