Skip to content

Commit

Permalink
fix(eslint-plugin): [member-naming] should match constructor args
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk committed Jul 29, 2019
1 parent f953cbd commit f66da5f
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 16 deletions.
73 changes: 57 additions & 16 deletions packages/eslint-plugin/src/rules/member-naming.ts
@@ -1,4 +1,7 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import {
TSESTree,
AST_NODE_TYPES,
} from '@typescript-eslint/experimental-utils';
import * as util from '../util';

interface Config<T = string> {
Expand Down Expand Up @@ -61,39 +64,77 @@ export default util.createRule<Options, MessageIds>({
return acc;
}, {});

function getParameterNode(
node: TSESTree.TSParameterProperty,
): TSESTree.Identifier | null {
if (node.parameter.type === AST_NODE_TYPES.AssignmentPattern) {
return node.parameter.left as TSESTree.Identifier;
}

if (node.parameter.type === AST_NODE_TYPES.Identifier) {
return node.parameter;
}

return null;
}

function validateParameterPropertyName(
node: TSESTree.TSParameterProperty,
): void {
const parameterNode = getParameterNode(node);
if (!parameterNode) {
return;
}

validateName(parameterNode, parameterNode.name, node.accessibility);
}

function validatePropertyName(
node: TSESTree.MethodDefinition | TSESTree.ClassProperty,
): void {
if (
node.type === AST_NODE_TYPES.MethodDefinition &&
node.kind === 'constructor'
) {
return;
}

validateName(
node.key,
util.getNameFromClassMember(node, sourceCode),
node.accessibility,
);
}

/**
* Check that the property name matches the convention for its
* accessibility.
* @param {ASTNode} node the named node to evaluate.
* Check that the name matches the convention for its accessibility.
* @param {ASTNode} node the named node to evaluate.
* @param {string} name
* @param {Modifiers} accessibility
* @returns {void}
* @private
*/
function validateName(
node: TSESTree.MethodDefinition | TSESTree.ClassProperty,
node: TSESTree.Identifier | TSESTree.Expression,
name: string,
accessibility: Modifiers = 'public',
): void {
const name = util.getNameFromClassMember(node, sourceCode);
const accessibility: Modifiers = node.accessibility || 'public';
const convention = conventions[accessibility];

const method = node as TSESTree.MethodDefinition;
if (method.kind === 'constructor') {
return;
}

if (!convention || convention.test(name)) {
return;
}

context.report({
node: node.key,
node,
messageId: 'incorrectName',
data: { accessibility, name, convention },
});
}

return {
MethodDefinition: validateName,
ClassProperty: validateName,
TSParameterProperty: validateParameterPropertyName,
MethodDefinition: validatePropertyName,
ClassProperty: validatePropertyName,
};
},
});
70 changes: 70 additions & 0 deletions packages/eslint-plugin/tests/rules/member-naming.test.ts
Expand Up @@ -86,6 +86,30 @@ class Class {
},
],
},

{
code: `
class Test {
constructor(public __a: string, protected __b: string, private __c: string = 100) {}
}
`,
options: [
{
protected: '^__',
private: '^__',
public: '^__',
},
],
},
{
code:
// Semantically invalid test case, TS has to throw an error.
`
class Foo {
constructor(private ...name: string[], private [test]: [string]) {}
}
`,
},
],
invalid: [
{
Expand Down Expand Up @@ -329,5 +353,51 @@ class Class {
},
],
},
{
code: `
class Test {
constructor(public a: string, protected b: string, private c: string = 100) {}
}
`,
options: [
{
public: '^__',
protected: '^__',
private: '^__',
},
],
errors: [
{
messageId: 'incorrectName',
data: {
accessibility: 'public',
convention: '/^__/',
name: 'a',
},
line: 3,
column: 24,
},
{
messageId: 'incorrectName',
data: {
accessibility: 'protected',
convention: '/^__/',
name: 'b',
},
line: 3,
column: 45,
},
{
messageId: 'incorrectName',
data: {
accessibility: 'private',
convention: '/^__/',
name: 'c',
},
line: 3,
column: 64,
},
],
},
],
});

0 comments on commit f66da5f

Please sign in to comment.