diff --git a/eslint.config.mjs b/eslint.config.mjs index 9f96b9169a72..700dd748d629 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -188,6 +188,7 @@ export default tseslint.config( // Internal repo rules // + '@typescript-eslint/internal/debug-namespace': 'error', '@typescript-eslint/internal/eqeq-nullish': 'error', '@typescript-eslint/internal/no-poorly-typed-ts-props': 'error', '@typescript-eslint/internal/no-relative-paths-to-internal-packages': diff --git a/packages/eslint-plugin-internal/src/rules/debug-namespace.ts b/packages/eslint-plugin-internal/src/rules/debug-namespace.ts new file mode 100755 index 000000000000..7aa735053f48 --- /dev/null +++ b/packages/eslint-plugin-internal/src/rules/debug-namespace.ts @@ -0,0 +1,65 @@ +import type { TSESTree } from '@typescript-eslint/utils'; + +import { getStaticValue } from '@typescript-eslint/utils/ast-utils'; + +import { createRule } from '../util'; + +function filePathToNamespace(filePath: string) { + const relativePath = filePath + .split(/packages[\\/]+/) + .slice(1) + .join(''); + + const relativeNamespace = relativePath + .replace(/^[\\/]/, '') + .replace(/(?:dist|lib|src)\//, '') + .replace(/\.\w+$/, '') + .replaceAll(/[^a-z0-9-]+/gi, ':'); + + return `typescript-eslint:${relativeNamespace}`; +} + +export default createRule({ + name: __filename, + meta: { + type: 'problem', + docs: { + description: + 'Enforce consistent debug() namespaces based on package name and file path', + }, + fixable: 'code', + messages: { + mismatched: + 'debug() namespace should match package and file. Use the fixer to update it.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const expected = filePathToNamespace(context.filename); + if (!expected) { + return {}; + } + + return { + 'VariableDeclarator[id.name="log"] > CallExpression[arguments.length=1][callee.name="debug"]'( + node: TSESTree.CallExpression, + ) { + const [argument] = node.arguments; + const staticValue = getStaticValue(argument); + if ( + typeof staticValue?.value !== 'string' || + staticValue.value === expected + ) { + return; + } + + context.report({ + node: argument, + messageId: 'mismatched', + fix: fixer => fixer.replaceText(argument, `'${expected}'`), + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin-internal/src/rules/index.ts b/packages/eslint-plugin-internal/src/rules/index.ts index bb5724b86c0d..806618f909e4 100644 --- a/packages/eslint-plugin-internal/src/rules/index.ts +++ b/packages/eslint-plugin-internal/src/rules/index.ts @@ -1,5 +1,6 @@ import type { Linter } from '@typescript-eslint/utils/ts-eslint'; +import debugNamespace from './debug-namespace'; import eqeqNullish from './eqeq-nullish'; import noPoorlyTypedTsProps from './no-poorly-typed-ts-props'; import noRelativePathsToInternalPackages from './no-relative-paths-to-internal-packages'; @@ -9,6 +10,7 @@ import pluginTestFormatting from './plugin-test-formatting'; import preferASTTypesEnum from './prefer-ast-types-enum'; export default { + 'debug-namespace': debugNamespace, 'eqeq-nullish': eqeqNullish, 'no-poorly-typed-ts-props': noPoorlyTypedTsProps, 'no-relative-paths-to-internal-packages': noRelativePathsToInternalPackages, diff --git a/packages/eslint-plugin-internal/tests/rules/debug-namespace.test.ts b/packages/eslint-plugin-internal/tests/rules/debug-namespace.test.ts new file mode 100644 index 000000000000..53e7ea81da15 --- /dev/null +++ b/packages/eslint-plugin-internal/tests/rules/debug-namespace.test.ts @@ -0,0 +1,75 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; + +import rule from '../../src/rules/debug-namespace'; + +const ruleTester = new RuleTester(); + +ruleTester.run('debug-namespace', rule, { + invalid: [ + { + code: "const log = debug('not:correct');", + errors: [ + { + column: 19, + endColumn: 32, + line: 1, + messageId: 'mismatched', + }, + ], + filename: 'typescript-eslint/packages/example/file.ts', + output: "const log = debug('typescript-eslint:example:file');", + }, + { + code: "const log = debug('not:correct');", + errors: [ + { + column: 19, + endColumn: 32, + line: 1, + messageId: 'mismatched', + }, + ], + filename: '/Users/example/typescript-eslint/packages/example/file.ts', + output: "const log = debug('typescript-eslint:example:file');", + }, + { + code: "const log = debug('not:correct');", + errors: [ + { + column: 19, + endColumn: 32, + line: 1, + messageId: 'mismatched', + }, + ], + filename: 'C:\\Code\\typescript-eslint\\packages\\example\\file.ts', + output: "const log = debug('typescript-eslint:example:file');", + }, + ], + valid: [ + { + code: "const log = debug('typescript-eslint:example:file');", + filename: 'typescript-eslint/packages/example/file.ts', + }, + { + code: "const logCustom = debug('typescript-eslint:example:file');", + filename: 'typescript-eslint/packages/example/file.ts', + }, + { + code: "const logCustom = debug('...');", + filename: 'typescript-eslint/packages/example/file.ts', + }, + { + code: "debug('...');", + filename: 'typescript-eslint/packages/example/file.ts', + }, + { + code: 'const log = debug(null);', + filename: 'typescript-eslint/packages/example/file.ts', + }, + { + code: 'const log = debug(123);', + filename: 'typescript-eslint/packages/example/file.ts', + }, + ], +}); diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index 3a4f451aaa1c..455174d4f214 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -4,7 +4,7 @@ import * as ts from 'typescript'; import { isTypeFlagSet } from './typeFlagUtils'; -const log = debug('typescript-eslint:eslint-plugin:utils:types'); +const log = debug('typescript-eslint:type-utils:predicates'); /** * Checks if the given type is (or accepts) nullable diff --git a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts index ab9144326b2b..e8fabb500574 100644 --- a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts +++ b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts @@ -7,7 +7,9 @@ import type { ASTAndDefiniteProgram } from './shared'; import { getScriptKind } from './getScriptKind'; import { createDefaultCompilerOptionsFromExtra } from './shared'; -const log = debug('typescript-eslint:typescript-estree:createIsolatedProgram'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:createIsolatedProgram', +); /** * @returns Returns a new source file and program corresponding to the linted code diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index e02c2c834385..00c8ae67c9bc 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -9,7 +9,9 @@ import { firstDefined } from '../node-utils'; import { createProjectProgramError } from './createProjectProgramError'; import { getAstFromProgram } from './shared'; -const log = debug('typescript-eslint:typescript-estree:createProjectProgram'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:createProjectProgram', +); /** * @param parseSettings Internal settings for parsing the file diff --git a/packages/typescript-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts index dfd753803231..26b2c5421612 100644 --- a/packages/typescript-estree/src/create-program/createProjectService.ts +++ b/packages/typescript-estree/src/create-program/createProjectService.ts @@ -10,7 +10,9 @@ import { validateDefaultProjectForFilesGlob } from './validateDefaultProjectForF const DEFAULT_PROJECT_MATCHED_FILES_THRESHOLD = 8; -const log = debug('typescript-eslint:typescript-estree:createProjectService'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:createProjectService', +); const logTsserverErr = debug( 'typescript-eslint:typescript-estree:tsserver:err', ); diff --git a/packages/typescript-estree/src/create-program/createSourceFile.ts b/packages/typescript-estree/src/create-program/createSourceFile.ts index 618e78645eba..83cdd455fd2b 100644 --- a/packages/typescript-estree/src/create-program/createSourceFile.ts +++ b/packages/typescript-estree/src/create-program/createSourceFile.ts @@ -7,7 +7,9 @@ import type { ASTAndNoProgram } from './shared'; import { isSourceFile } from '../source-files'; import { getScriptKind } from './getScriptKind'; -const log = debug('typescript-eslint:typescript-estree:createSourceFile'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:createSourceFile', +); function createSourceFile(parseSettings: ParseSettings): ts.SourceFile { log( diff --git a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts index 1aefcaa87592..ee9dba7a88ad 100644 --- a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts +++ b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts @@ -14,7 +14,9 @@ import { getCanonicalFileName, } from './shared'; -const log = debug('typescript-eslint:typescript-estree:createWatchProgram'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:getWatchProgramsForProjects', +); /** * Maps tsconfig paths to their corresponding file contents and resulting watches diff --git a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts index 660e82ac4d76..eb6760db4323 100644 --- a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts +++ b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts @@ -8,7 +8,9 @@ import type { ASTAndDefiniteProgram } from './shared'; import { getParsedConfigFile } from './getParsedConfigFile'; import { getAstFromProgram } from './shared'; -const log = debug('typescript-eslint:typescript-estree:useProvidedProgram'); +const log = debug( + 'typescript-eslint:typescript-estree:create-program:useProvidedPrograms', +); function useProvidedPrograms( programInstances: Iterable, diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 891e5dee5c51..6a1c42008c6b 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -19,7 +19,7 @@ import { resolveProjectList } from './resolveProjectList'; import { warnAboutTSVersion } from './warnAboutTSVersion'; const log = debug( - 'typescript-eslint:typescript-estree:parser:parseSettings:createParseSettings', + 'typescript-eslint:typescript-estree:parseSettings:createParseSettings', ); let TSCONFIG_MATCH_CACHE: ExpiringCache | null; diff --git a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts index 6c79d0619f86..f7f3dc8851b8 100644 --- a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts +++ b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts @@ -5,7 +5,9 @@ import * as path from 'node:path'; import type { TSESTreeOptions } from '../parser-options'; import type { ParseSettings } from './index'; -const log = debug('typescript-eslint:typescript-estree:getProjectConfigFiles'); +const log = debug( + 'typescript-eslint:typescript-estree:parseSettings:getProjectConfigFiles', +); /** * Checks for a matching TSConfig to a file including its parent directories, diff --git a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts index 8d9bd6bb6f9e..f29bf0026469 100644 --- a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts +++ b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts @@ -16,7 +16,7 @@ import { } from './ExpiringCache'; const log = debug( - 'typescript-eslint:typescript-estree:parser:parseSettings:resolveProjectList', + 'typescript-eslint:typescript-estree:parseSettings:resolveProjectList', ); let RESOLUTION_CACHE: ExpiringCache<