diff --git a/docs/api/ast-utils.md b/docs/api/ast-utils.md index 0edfcf6..be0fd11 100644 --- a/docs/api/ast-utils.md +++ b/docs/api/ast-utils.md @@ -214,16 +214,19 @@ Get the name and kind of a given function node. - `({ foo: function() {} })` ........................ `method 'foo'` - `({ ['foo']: function() {} })` .................... `method 'foo'` - `({ [foo]: function() {} })` ...................... `method` +- `({ [foo]: function() {} })` ...................... `method [foo]` if sourceCode is present. - `({ foo() {} })` .................................. `method 'foo'` - `({ foo: function* foo() {} })` ................... `generator method 'foo'` - `({ foo: function*() {} })` ....................... `generator method 'foo'` - `({ ['foo']: function*() {} })` ................... `generator method 'foo'` - `({ [foo]: function*() {} })` ..................... `generator method` +- `({ [foo]: function*() {} })` ..................... `generator method [foo]` if sourceCode is present. - `({ *foo() {} })` ................................. `generator method 'foo'` - `({ foo: async function foo() {} })` .............. `async method 'foo'` - `({ foo: async function() {} })` .................. `async method 'foo'` - `({ ['foo']: async function() {} })` .............. `async method 'foo'` - `({ [foo]: async function() {} })` ................ `async method` +- `({ [foo]: async function() {} })` ................ `async method [foo]` if sourceCode is present. - `({ async foo() {} })` ............................ `async method 'foo'` - `({ get foo() {} })` .............................. `getter 'foo'` - `({ set foo(a) {} })` ............................. `setter 'foo'` @@ -235,8 +238,11 @@ Get the name and kind of a given function node. - `class A { *['foo']() {} }` ....................... `generator method 'foo'` - `class A { async ['foo']() {} }` .................. `async method 'foo'` - `class A { [foo]() {} }` .......................... `method` +- `class A { [foo]() {} }` .......................... `method [foo]` if sourceCode is present. - `class A { *[foo]() {} }` ......................... `generator method` +- `class A { *[foo]() {} }` ......................... `generator method [foo]` if sourceCode is present. - `class A { async [foo]() {} }` .................... `async method` +- `class A { async [foo]() {} }` .................... `async method [foo]` if sourceCode is present. - `class A { get foo() {} }` ........................ `getter 'foo'` - `class A { set foo(a) {} }` ....................... `setter 'foo'` - `class A { static foo() {} }` ..................... `static method 'foo'` @@ -244,22 +250,24 @@ Get the name and kind of a given function node. - `class A { static async foo() {} }` ............... `static async method 'foo'` - `class A { static get foo() {} }` ................. `static getter 'foo'` - `class A { static set foo(a) {} }` ................ `static setter 'foo'` -- `class A { #foo() {} }` ........................... `private method '#foo'` -- `class A { *#foo() {} }` .......................... `private generator method '#foo'` -- `class A { async #foo() {} }` ..................... `private async method '#foo'` -- `class A { get #foo() {} }` ....................... `private getter '#foo'` -- `class A { set #foo(a) {} }` ...................... `private setter '#foo'` -- `class A { static #foo() {} }` .................... `private static method '#foo'` -- `class A { static *#foo() {} }` ................... `private static generator method '#foo'` -- `class A { static async #foo() {} }` .............. `private static async method '#foo'` -- `class A { static get #foo() {} }` ................ `private static getter '#foo'` -- `class A { static set #foo(a) {} }` ............... `private static setter '#foo'` -- `class A { #foo = function() {} }` ................ `private method '#foo'"` -- `class A { #foo = function*() {} }` ............... `private generator method '#foo'"` -- `class A { #foo = async function() {} }` .......... `private async method '#foo'"` -- `class A { static #foo = function() {} }` ......... `private static method '#foo'"` -- `class A { static #foo = function*() {} }` ........ `private static generator method '#foo'"` -- `class A { static #foo = async function() {} }` ... `private static async method '#foo'"` +- `class A { #foo() {} }` ........................... `private method #foo` +- `class A { '#foo'() {} }` ......................... `method '#foo'` +- `class A { *#foo() {} }` .......................... `private generator method #foo` +- `class A { async #foo() {} }` ..................... `private async method #foo` +- `class A { get #foo() {} }` ....................... `private getter #foo` +- `class A { set #foo(a) {} }` ...................... `private setter #foo` +- `class A { static #foo() {} }` .................... `static private method #foo` +- `class A { static *#foo() {} }` ................... `static private generator method #foo` +- `class A { static async #foo() {} }` .............. `static private async method #foo` +- `class A { static get #foo() {} }` ................ `static private getter #foo` +- `class A { static set #foo(a) {} }` ............... `static private setter #foo` +- `class A { '#foo' = function() {} }` .............. `method '#foo'"` +- `class A { #foo = function() {} }` ................ `private method #foo"` +- `class A { #foo = function*() {} }` ............... `private generator method #foo"` +- `class A { #foo = async function() {} }` .......... `private async method #foo"` +- `class A { static #foo = function() {} }` ......... `static private method #foo"` +- `class A { static #foo = function*() {} }` ........ `static private generator method #foo"` +- `class A { static #foo = async function() {} }` ... `static private async method #foo"` ``` @@ -269,6 +277,7 @@ Get the name and kind of a given function node. Name | Type | Description :-----|:-----|:------------ node | Node | The function node to get the name and kind. This should be any of `FunctionDeclaration`, `FunctionExpression`, and `ArrowFunctionExpression` node. +sourceCode | SourceCode | Optional. The source code object to get the text of computed property keys. ### Return value @@ -282,12 +291,14 @@ const { getFunctionNameWithKind } = require("eslint-utils") module.exports = { meta: {}, create(context) { + const sourceCode = context.getSourceCode() + return { FunctionDeclaration(node) { context.report({ node, message: "disallow this {{name}}!", - data: { name: getFunctionNameWithKind(node) } + data: { name: getFunctionNameWithKind(node, sourceCode) } }) }, } diff --git a/src/get-function-name-with-kind.js b/src/get-function-name-with-kind.js index 80437e4..fa58adc 100644 --- a/src/get-function-name-with-kind.js +++ b/src/get-function-name-with-kind.js @@ -3,23 +3,27 @@ import { getPropertyName } from "./get-property-name" /** * Get the name and kind of the given function node. * @param {ASTNode} node - The function node to get. + * @param {SourceCode} [sourceCode] The source code object to get the code of computed property keys. * @returns {string} The name and kind of the function node. */ -export function getFunctionNameWithKind(node) { +// eslint-disable-next-line complexity +export function getFunctionNameWithKind(node, sourceCode) { const parent = node.parent const tokens = [] - const isFieldDefinition = - parent.type === "MethodDefinition" || - parent.type === "PropertyDefinition" - let privateName = null - if (isFieldDefinition) { - if (parent.key.type === "PrivateIdentifier") { - privateName = `#${parent.key.name}` - tokens.push("private") - } + const isObjectMethod = parent.type === "Property" && parent.value === node + const isClassMethod = + parent.type === "MethodDefinition" && parent.value === node + const isClassFieldMethod = + parent.type === "PropertyDefinition" && parent.value === node + + // Modifiers. + if (isClassMethod || isClassFieldMethod) { if (parent.static) { tokens.push("static") } + if (parent.key.type === "PrivateIdentifier") { + tokens.push("private") + } } if (node.async) { tokens.push("async") @@ -28,9 +32,8 @@ export function getFunctionNameWithKind(node) { tokens.push("generator") } - if (node.type === "ArrowFunctionExpression") { - tokens.push("arrow", "function") - } else if (parent.type === "Property" || isFieldDefinition) { + // Kinds. + if (isObjectMethod || isClassMethod) { if (parent.kind === "constructor") { return "constructor" } @@ -41,38 +44,45 @@ export function getFunctionNameWithKind(node) { } else { tokens.push("method") } + } else if (isClassFieldMethod) { + tokens.push("method") } else { + if (node.type === "ArrowFunctionExpression") { + tokens.push("arrow") + } tokens.push("function") } - if (node.id) { - tokens.push(`'${node.id.name}'`) - } else { - const name = privateName || getPropertyName(parent) - - if (name) { - tokens.push(`'${name}'`) + // Names. + if (isObjectMethod || isClassMethod || isClassFieldMethod) { + if (parent.key.type === "PrivateIdentifier") { + tokens.push(`#${parent.key.name}`) + } else { + const name = getPropertyName(parent) + if (name) { + tokens.push(`'${name}'`) + } else if (sourceCode) { + const keyText = sourceCode.getText(parent.key) + if (!keyText.includes("\n")) { + tokens.push(`[${keyText}]`) + } + } } - } - - if ( - node.type === "ArrowFunctionExpression" || - (node.type === "FunctionExpression" && node.id === null) + } else if (node.id) { + tokens.push(`'${node.id.name}'`) + } else if ( + parent.type === "VariableDeclarator" && + parent.id && + parent.id.type === "Identifier" ) { - if ( - parent.type === "VariableDeclarator" && - parent.id && - parent.id.type === "Identifier" - ) { - tokens.push(`'${parent.id.name}'`) - } - if ( - parent.type === "AssignmentExpression" && - parent.left && - parent.left.type === "Identifier" - ) { - tokens.push(`'${parent.left.name}'`) - } + tokens.push(`'${parent.id.name}'`) + } else if ( + (parent.type === "AssignmentExpression" || + parent.type === "AssignmentPattern") && + parent.left && + parent.left.type === "Identifier" + ) { + tokens.push(`'${parent.left.name}'`) } return tokens.join(" ") diff --git a/test/get-function-name-with-kind.js b/test/get-function-name-with-kind.js index f25e541..ffc9f22 100644 --- a/test/get-function-name-with-kind.js +++ b/test/get-function-name-with-kind.js @@ -34,17 +34,17 @@ describe("The 'getFunctionNameWithKind' function", () => { "({ foo: function foo() {} })": "method 'foo'", "({ foo: function() {} })": "method 'foo'", "({ ['foo']: function() {} })": "method 'foo'", - "({ [foo]: function() {} })": "method", + "({ [foo]: function() {} })": "method [foo]", "({ foo() {} })": "method 'foo'", "({ foo: function* foo() {} })": "generator method 'foo'", "({ foo: function*() {} })": "generator method 'foo'", "({ ['foo']: function*() {} })": "generator method 'foo'", - "({ [foo]: function*() {} })": "generator method", + "({ [foo]: function*() {} })": "generator method [foo]", "({ *foo() {} })": "generator method 'foo'", "({ foo: async function foo() {} })": "async method 'foo'", "({ foo: async function() {} })": "async method 'foo'", "({ ['foo']: async function() {} })": "async method 'foo'", - "({ [foo]: async function() {} })": "async method", + "({ [foo]: async function() {} })": "async method [foo]", "({ async foo() {} })": "async method 'foo'", "({ get foo() {} })": "getter 'foo'", "({ set foo(a) {} })": "setter 'foo'", @@ -55,9 +55,9 @@ describe("The 'getFunctionNameWithKind' function", () => { "class A { ['foo']() {} }": "method 'foo'", "class A { *['foo']() {} }": "generator method 'foo'", "class A { async ['foo']() {} }": "async method 'foo'", - "class A { [foo]() {} }": "method", - "class A { *[foo]() {} }": "generator method", - "class A { async [foo]() {} }": "async method", + "class A { [foo]() {} }": "method [foo]", + "class A { *[foo]() {} }": "generator method [foo]", + "class A { async [foo]() {} }": "async method [foo]", "class A { get foo() {} }": "getter 'foo'", "class A { set foo(a) {} }": "setter 'foo'", "class A { static foo() {} }": "static method 'foo'", @@ -69,64 +69,67 @@ describe("The 'getFunctionNameWithKind' function", () => { semver.gte(eslint.CLIEngine.version, "7.0.0") ? { - "class A { #foo() {} }": "private method '#foo'", - "class A { *#foo() {} }": "private generator method '#foo'", - "class A { async #foo() {} }": "private async method '#foo'", - "class A { get #foo() {} }": "private getter '#foo'", - "class A { set #foo(a) {} }": "private setter '#foo'", - "class A { static #foo() {} }": - "private static method '#foo'", + "class A { #foo() {} }": "private method #foo", + "class A { '#foo'() {} }": "method '#foo'", + "class A { *#foo() {} }": "private generator method #foo", + "class A { async #foo() {} }": "private async method #foo", + "class A { get #foo() {} }": "private getter #foo", + "class A { set #foo(a) {} }": "private setter #foo", + "class A { static #foo() {} }": "static private method #foo", "class A { static *#foo() {} }": - "private static generator method '#foo'", + "static private generator method #foo", "class A { static async #foo() {} }": - "private static async method '#foo'", + "static private async method #foo", "class A { static get #foo() {} }": - "private static getter '#foo'", + "static private getter #foo", "class A { static set #foo(a) {} }": - "private static setter '#foo'", + "static private setter #foo", "class A { foo = function() {} }": "method 'foo'", - "class A { foo = () => {} }": "arrow function 'foo'", + "class A { foo = () => {} }": "method 'foo'", "class A { foo = function*() {} }": "generator method 'foo'", "class A { foo = async function() {} }": "async method 'foo'", "class A { ['foo'] = function() {} }": "method 'foo'", - "class A { ['foo'] = () => {} }": "arrow function 'foo'", + "class A { ['foo'] = () => {} }": "method 'foo'", "class A { ['foo'] = function*() {} }": "generator method 'foo'", "class A { ['foo'] = async function() {} }": "async method 'foo'", - "class A { [foo] = function() {} }": "method", - "class A { [foo] = () => {} }": "arrow function", - "class A { [foo] = function*() {} }": "generator method", - "class A { [foo] = async function() {} }": "async method", + "class A { [foo] = function() {} }": "method [foo]", + "class A { [foo] = () => {} }": "method [foo]", + "class A { [foo] = function*() {} }": + "generator method [foo]", + "class A { [foo] = async function() {} }": + "async method [foo]", "class A { static foo = function() {} }": "static method 'foo'", - "class A { static foo = () => {} }": - "static arrow function 'foo'", + "class A { static foo = () => {} }": "static method 'foo'", "class A { static foo = function*() {} }": "static generator method 'foo'", "class A { static foo = async function() {} }": "static async method 'foo'", - "class A { #foo = function() {} }": "private method '#foo'", - "class A { #foo = () => {} }": - "private arrow function '#foo'", + "class A { #foo = function() {} }": "private method #foo", + "class A { #foo = () => {} }": "private method #foo", "class A { #foo = function*() {} }": - "private generator method '#foo'", + "private generator method #foo", "class A { #foo = async function() {} }": - "private async method '#foo'", + "private async method #foo", "class A { static #foo = function() {} }": - "private static method '#foo'", + "static private method #foo", "class A { static #foo = () => {} }": - "private static arrow function '#foo'", + "static private method #foo", "class A { static #foo = function*() {} }": - "private static generator method '#foo'", + "static private generator method #foo", "class A { static #foo = async function() {} }": - "private static async method '#foo'", + "static private async method #foo", } : {} ) for (const key of Object.keys(expectedResults)) { - it(`should return "${expectedResults[key]}" for "${key}".`, () => { + const expectedResult1 = expectedResults[key].replace(/\s+\[.+?\]/gu, "") + const expectedResult2 = expectedResults[key] + + it(`should return "${expectedResult1}" for "${key}".`, () => { const linter = new eslint.Linter() let actualResult = null @@ -149,7 +152,36 @@ describe("The 'getFunctionNameWithKind' function", () => { 0, messages[0] && messages[0].message ) - assert.strictEqual(actualResult, expectedResults[key]) + assert.strictEqual(actualResult, expectedResult1) + }) + + it(`should return "${expectedResult2}" for "${key}" if sourceCode is present.`, () => { + const linter = new eslint.Linter() + + let actualResult = null + linter.defineRule("test", context => ({ + ":function"(node) { + actualResult = getFunctionNameWithKind( + node, + context.getSourceCode() + ) + }, + })) + const messages = linter.verify(key, { + rules: { test: "error" }, + parserOptions: { + ecmaVersion: semver.gte(eslint.CLIEngine.version, "7.0.0") + ? 2022 + : 2018, + }, + }) + + assert.strictEqual( + messages.length, + 0, + messages[0] && messages[0].message + ) + assert.strictEqual(actualResult, expectedResult2) }) } })