From 3d6b6f747575aa77996f0b2a03159e58fa4f0c16 Mon Sep 17 00:00:00 2001 From: Zqc <24S103279@stu.hit.edu.cn> Date: Mon, 22 Jun 2026 10:10:07 +0800 Subject: [PATCH] feat(lint): enforce kebab-case filenames with ESLint (#4797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 俊良 --- AGENTS.md | 2 +- eslint.config.js | 20 ++ eslint.legacy-filenames.mjs | 525 ++++++++++++++++++++++++++++++++++++ package-lock.json | 31 +++ package.json | 1 + 5 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 eslint.legacy-filenames.mjs diff --git a/AGENTS.md b/AGENTS.md index c0cd3825a5e..6ef17e0ab18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -120,7 +120,7 @@ npm run preflight # Full check: clean → install → format → lint → build - **Tests**: Collocated with source (`file.test.ts` next to `file.ts`), vitest framework - **File naming**: `PascalCase.tsx` for React components, `kebab-case.ts` for - new non-component files. Leave existing `camelCase` files alone — renaming breaks `git blame` and imports. + `.ts` files in `packages/core` and `packages/cli` (enforced by ESLint). Existing camelCase files are allowlisted in `eslint.legacy-filenames.mjs`; rename opportunistically when touching them, updating all imports in the same commit (note: renames lose `git blame` history). - **Comments**: Default to none. Add only when _why_ is non-obvious; don't delete existing ones as cleanup. - **Commits**: Conventional Commits (e.g., `feat(cli): Add --json flag`) - **Node.js**: Development and production both require `>=22` (Ink 7 + React 19.2 requirement) diff --git a/eslint.config.js b/eslint.config.js index a27c07f8219..41da3e00487 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -14,6 +14,8 @@ import vitest from '@vitest/eslint-plugin'; import globals from 'globals'; // For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format import storybook from 'eslint-plugin-storybook'; +import checkFile from 'eslint-plugin-check-file'; +import { legacyFilenames } from './eslint.legacy-filenames.mjs'; export default tseslint.config( { @@ -166,6 +168,24 @@ export default tseslint.config( 'default-case': 'error', }, }, + { + // Enforce kebab-case filenames + files: ['packages/core/src/**/*.ts', 'packages/cli/src/**/*.ts'], + ignores: legacyFilenames.flatMap((name) => [ + `**/${name}.ts`, + `**/${name}.*.ts`, + ]), + plugins: { + 'check-file': checkFile, + }, + rules: { + 'check-file/filename-naming-convention': [ + 'error', + { '**/*.ts': 'KEBAB_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + }, { files: ['packages/*/src/**/*.test.{ts,tsx}', 'packages/**/test/**/*.test.{ts,tsx}'], plugins: { diff --git a/eslint.legacy-filenames.mjs b/eslint.legacy-filenames.mjs new file mode 100644 index 00000000000..08d0c98777d --- /dev/null +++ b/eslint.legacy-filenames.mjs @@ -0,0 +1,525 @@ +/** + * Legacy non-kebab-case filenames in packages/core/src and packages/cli/src. + * + * Used by eslint.config.js to exclude these files from the kebab-case rule. + * Remove entries as files are renamed in migration PRs. + * + * See: https://github.com/QwenLM/qwen-code/issues/4419 + */ +export const legacyFilenames = [ + 'abortController', + 'aboutCommand', + 'acpAgent', + 'acpAgent.worktree', + 'acpModelUtils', + 'activeGoalStore', + 'agentHistoryAdapter', + 'agentsCommand', + 'anthropicContentGenerator', + 'apiPreconnect', + 'approvalModeCommand', + 'approvalModeDisplay', + 'approvalModeVisuals', + 'ArenaAgentClient', + 'arenaCommand', + 'arenaCommand.agentComplete', + 'ArenaManager', + 'argumentProcessor', + 'AsciiArt', + 'askUserQuestion', + 'asyncHookRegistry', + 'asyncMessageQueue', + 'atCommandProcessor', + 'atFileProcessor', + 'atomicFileWrite', + 'attributionTrailer', + 'authCommand', + 'authMethods', + 'authPreflight', + 'autoMode', + 'availableModels', + 'backgroundShellRegistry', + 'backgroundWorkUtils', + 'bareMode', + 'baseController', + 'BaseEmitter', + 'BaseJsonOutputAdapter', + 'baseLlmClient', + 'branchCommand', + 'btwCommand', + 'bugCommand', + 'BuiltinCommandLoader', + 'BundledSkillLoader', + 'bundlePaths', + 'chatCompressionService', + 'chatRecordingService', + 'chatRecordingService.autoTitle', + 'chatRecordingService.customTitle', + 'clearCommand', + 'clipboardUtils', + 'commandMetadata', + 'CommandService', + 'commandUtils', + 'commentJson', + 'commitAttribution', + 'compactionInputSlimming', + 'compressCommand', + 'computeStats', + 'configResolver', + 'contentGenerator', + 'contextCommand', + 'contextLengthError', + 'ControlContext', + 'ControlDispatcher', + 'ControlService', + 'copyCommand', + 'coreToolScheduler', + 'costCalculator', + 'cpuProfiler', + 'crawlCache', + 'cronDisplay', + 'cronParser', + 'cronScheduler', + 'customBanner', + 'customMatchers', + 'daemonStatusProvider', + 'DaemonTuiAdapter', + 'dangerousRules', + 'DataProcessor', + 'debugLogger', + 'debugMode', + 'deepMerge', + 'deleteCommand', + 'denialTracking', + 'detectTodoChanges', + 'deviceFlow', + 'dialogScopeUtils', + 'diffCommand', + 'diffOptions', + 'displayUtils', + 'docsCommand', + 'doctorChecks', + 'doctorCommand', + 'dreamAgentPlanner', + 'dreamCommand', + 'DualOutputBridge', + 'earlyInputCapture', + 'editHelper', + 'editorCommand', + 'editorSettingsManager', + 'envInterpolator', + 'environmentContext', + 'envSnapshot', + 'envVarResolver', + 'errorCodes', + 'errorHandler', + 'errorParsing', + 'errorReporting', + 'eventBus', + 'exitPlanMode', + 'exportCommand', + 'extensionManager', + 'extensionsCommand', + 'extensionSettings', + 'extractAgent', + 'extractionAgentPlanner', + 'extractionPlanner', + 'FileCommandLoader', + 'FileCommandLoader-extension', + 'FileCommandLoader-markdown', + 'fileDiscoveryService', + 'fileHistoryService', + 'fileReadCache', + 'fileSearch', + 'fileSystemService', + 'fileUtils', + 'followupState', + 'forgetCommand', + 'forkedAgent', + 'forkedAgent.agent', + 'forkedAgent.cache', + 'functionHookRunner', + 'geminiChat', + 'geminiContentGenerator', + 'geminiRequest', + 'generateContentResponseUtilities', + 'generatedFiles', + 'getFolderStructure', + 'getPty', + 'gitDiff', + 'gitIgnoreParser', + 'gitInit', + 'gitService', + 'gitUtils', + 'gitWorktreeService', + 'gitWorktreeService.hooks.integ', + 'gitWorktreeService.symlinks.integ', + 'goalCommand', + 'goalHook', + 'goalJudge', + 'goalLoop', + 'gradientUtils', + 'handleAutoUpdate', + 'headlessSafetyWarnings', + 'helpCommand', + 'historyMapping', + 'HistoryReplayer', + 'historyUtils', + 'hookAggregator', + 'hookController', + 'hookEventHandler', + 'hookPlanner', + 'hookRegistry', + 'hookRunner', + 'hooksCommand', + 'hookSystem', + 'httpAcpBridge', + 'httpHookRunner', + 'iconvHelper', + 'ideCommand', + 'ideContext', + 'ignorePatterns', + 'imageTokenizer', + 'initCommand', + 'injectionParser', + 'inMemoryChannel', + 'InProcessBackend', + 'insightCommand', + 'insightProtocol', + 'installationInfo', + 'installationManager', + 'internalPromptIds', + 'isNarrowWidth', + 'ITermBackend', + 'JsonOutputAdapter', + 'jsonSchemaArg', + 'keyBindings', + 'keyMatchers', + 'keyToAnsi', + 'kittyProtocolDetector', + 'languageCommand', + 'languageUtils', + 'lastInteractionAt', + 'latexRenderer', + 'layoutUtils', + 'LlmRewriter', + 'loadedSettingsAdapter', + 'loggingContentGenerator', + 'loopbackBinds', + 'loopDetectionService', + 'lowlightLoader', + 'LruCache', + 'lspCommand', + 'LspConfigLoader', + 'LspConnectionFactory', + 'LspResponseNormalizer', + 'LspServerManager', + 'markdownUtilities', + 'matcherGrouping', + 'mcpCommand', + 'McpPromptLoader', + 'memoryAge', + 'memoryCommand', + 'memoryDiagnostics', + 'memoryDiagnosticsDumper', + 'memoryDiscovery', + 'memoryImportProcessor', + 'memoryLifecycle', + 'memoryPressureMonitor', + 'mergeCompactToolGroups', + 'mermaidImageRenderer', + 'mermaidVisualRenderer', + 'MessageEmitter', + 'messageInspectors', + 'MessageRewriteMiddleware', + 'mockCommandContext', + 'mockWorkspaceContext', + 'modalityDefaults', + 'modelCommand', + 'modelConfigErrors', + 'modelConfigResolver', + 'modelConfigUtils', + 'modelId', + 'modelProvidersScope', + 'modelRegistry', + 'modelsBySource', + 'modelsConfig', + 'monitorRegistry', + 'mustTranslateKeys', + 'NativeLspClient', + 'NativeLspService', + 'nextSpeakerChecker', + 'nonInteractiveCli', + 'nonInteractiveCliCommands', + 'nonInteractiveHelpers', + 'nonInteractiveToolExecutor', + 'nonInteractiveUi', + 'notificationService', + 'openaiContentGenerator', + 'openaiLogger', + 'openaiTimeoutHandling', + 'orphanedToolCallsTest', + 'overlayFs', + 'partUtils', + 'pathReader', + 'permissionController', + 'permissionFlow', + 'permissionsCommand', + 'permissionUtils', + 'planCommand', + 'PlanEmitter', + 'platformConstants', + 'postCompactAttachments', + 'priorReadEnforcement', + 'processUtils', + 'projectPath', + 'projectRoot', + 'projectSummary', + 'promptHookIntegration', + 'promptHookRunner', + 'promptIdContext', + 'proxyUtils', + 'QualitativeInsightTypes', + 'quitCommand', + 'quotaErrorDetection', + 'qwenContentGenerator', + 'qwenDeviceFlowProvider', + 'qwenIgnoreParser', + 'qwenOAuth2', + 'rateLimit', + 'readManyFiles', + 'readStdin', + 'recapCommand', + 'registerSkillHooks', + 'relevanceSelector', + 'rememberCommand', + 'RemoteInputWatcher', + 'renameCommand', + 'requestCaptureContext', + 'requestTokenizer', + 'resolvePath', + 'responseParsingOptions', + 'restoreCommand', + 'restoreGoal', + 'resumeCommand', + 'resumeHistoryUtils', + 'retryContext', + 'rewindCommand', + 'ripGrep', + 'ripgrepUtils', + 'rulesDiscovery', + 'runBudget', + 'runQwenServe', + 'runtimeDiagnostics', + 'runtimeFetchOptions', + 'runtimeOutputDirContext', + 'runtimeStatus', + 'runtimeStatus.config', + 'safeJsonParse', + 'safeJsonStringify', + 'sandboxConfig', + 'schemaConverter', + 'schemaValidator', + 'sdkMcpController', + 'serviceAPIs', + 'Session', + 'Session.worktree', + 'sessionHooksManager', + 'sessionPaths', + 'sessionPickerUtils', + 'sessionRecap', + 'sessionService', + 'sessionService.corruption', + 'sessionService.rename', + 'sessionStorageUtils', + 'sessionTitle', + 'settingsCommand', + 'settingsSchema', + 'settingsUtils', + 'setupGithubCommand', + 'sharedTokenManager', + 'shellAstParser', + 'shellCommandProcessor', + 'shellContextEnv', + 'shellExecutionService', + 'shellProcessor', + 'shellReadOnlyChecker', + 'sideQuery', + 'SkillCommandLoader', + 'skillReviewAgentPlanner', + 'skillReviewNudge', + 'skillsCommand', + 'slashCommandProcessor', + 'sourceLabels', + 'spawnWrapper', + 'speculationToolGate', + 'ssrfGuard', + 'startupEventSink', + 'startupProfiler', + 'startupWarnings', + 'StaticInsightGenerator', + 'StaticInsightTypes', + 'statsCommand', + 'statuslineCommand', + 'statusLinePresets', + 'stdioHelpers', + 'stopHookCap', + 'streamContentDetection', + 'streamingToolCallParser', + 'StreamJsonInputReader', + 'StreamJsonOutputAdapter', + 'StreamJsonOutputAdapter.dualOutput', + 'subagentGenerator', + 'subagentNameContext', + 'SubAgentTracker', + 'suggestionGenerator', + 'summaryCommand', + 'supportedImageFormats', + 'symlinkScope', + 'synchronizedOutput', + 'syntheticOutput', + 'systemController', + 'systemEncoding', + 'systemInfo', + 'systemInfoFields', + 'taggedThinkingParser', + 'tasksCommand', + 'TemplateRenderer', + 'terminalRedrawOptimizer', + 'terminalSafe', + 'terminalSerializer', + 'terminalSetup', + 'terminalSetupCommand', + 'testUtils', + 'textConstants', + 'textTokenizer', + 'textUtils', + 'themeCommand', + 'thoughtUtils', + 'throttledOnce', + 'tipHistory', + 'tipRegistry', + 'Tips', + 'tipScheduler', + 'TmuxBackend', + 'toAutoClassifierInput', + 'todoSnapshot', + 'todoWrite', + 'tokenEstimation', + 'tokenLimits', + 'ToolCallEmitter', + 'toolHookTriggers', + 'toolsCommand', + 'toolUseSummary', + 'translationDict', + 'truncatedDiffPreview', + 'trustCommand', + 'trustedFolders', + 'trustedHooks', + 'TurnBuffer', + 'uiTelemetry', + 'updateCheck', + 'updateEventEmitter', + 'urlValidator', + 'useAgentsManagerDialog', + 'useAgentStreamingState', + 'useAnimatedScrollbar', + 'useAnimationFrame', + 'useApprovalModeCommand', + 'useArenaCommand', + 'useArenaInProcess', + 'useAtCompletion', + 'useAttentionNotifications', + 'useAuth', + 'useAutoAcceptIndicator', + 'useAwaySummary', + 'useBackgroundTaskView', + 'useBatchedScroll', + 'useBracketedPaste', + 'useBranchCommand', + 'useCommandCompletion', + 'useCommandMigration', + 'useCompletion', + 'useConfigInitMessage', + 'useContextualTips', + 'useDeleteCommand', + 'useDialogClose', + 'useDiffData', + 'useDoublePress', + 'useEditorSettings', + 'useExportCompletion', + 'useExtensionsManagerDialog', + 'useExtensionUpdates', + 'useFeedbackDialog', + 'useFocus', + 'useFolderTrust', + 'useGeminiStream', + 'useGitBranchName', + 'useHistoryManager', + 'useHooksDialog', + 'useIdeTrustListener', + 'useInitializationAuthError', + 'useInputHistory', + 'useInputHistoryStore', + 'useKeypress', + 'useKittyKeyboardProtocol', + 'useLaunchEditor', + 'useLoadingIndicator', + 'useLogger', + 'useMcpDialog', + 'useMCPHealth', + 'useMemoryDialog', + 'useMemoryMonitor', + 'useMessageQueue', + 'useModelCommand', + 'useMouseEvents', + 'usePhraseCycler', + 'usePreferredEditor', + 'useProviderSetupFlow', + 'useProviderUpdates', + 'useQwenAuth', + 'useReactToolScheduler', + 'useRefreshMemoryCommand', + 'useResumeCommand', + 'userPromptExpansionHook', + 'userStartupWarnings', + 'useSelectionList', + 'useSessionPicker', + 'useSessionSearchInput', + 'useSettingsCommand', + 'useShellHistory', + 'useShowMemoryCommand', + 'useSkillsManagerDialog', + 'useSlashCompletion', + 'useStateAndRef', + 'useStatusLine', + 'useSubagentCreateDialog', + 'useTerminalNotification', + 'useTerminalProgress', + 'useTerminalSize', + 'useThemeCommand', + 'useTimer', + 'useTomlMigration', + 'useToolScheduler', + 'useTrustModify', + 'useTurnDiffs', + 'useWelcomeBack', + 'useWorktreeSession', + 'validateNonInterActiveAuth', + 'variableSchema', + 'vimCommand', + 'warningHandler', + 'windowsPath', + 'windowTitle', + 'workspaceAgents', + 'workspaceContext', + 'workspaceFileRead', + 'workspaceFileSystem', + 'workspaceFileWrite', + 'workspaceMemory', + 'worktreeCleanup', + 'worktreeSessionService', + 'worktreeStartup', + 'writeContextFile', + 'writeWithBackup', +]; diff --git a/package-lock.json b/package-lock.json index 338470f354a..aa62d66db6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "esbuild-plugin-wasm": "^1.1.0", "eslint": "^9.24.0", "eslint-config-prettier": "^10.1.2", + "eslint-plugin-check-file": "^3.3.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-license-header": "^0.8.0", "eslint-plugin-react": "^7.37.5", @@ -10345,6 +10346,36 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-check-file": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-check-file/-/eslint-plugin-check-file-3.3.1.tgz", + "integrity": "sha512-b7fDkp8Y0T9vloTYSrkMnb5Cqpk1bc6+jDQdgKIhzdwb47NmLAvMFYo3SzHOPF8u4SEsOVhmrNSbo6Sp633syg==", + "dev": true, + "funding": [ + { + "type": "ko_fi", + "url": "https://ko-fi.com/huanluo" + }, + { + "type": "github", + "url": "https://github.com/sponsors/dukeluo" + } + ], + "license": "Apache-2.0", + "workspaces": [ + "examples/basic" + ], + "dependencies": { + "is-glob": "^4.0.3", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, "node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", diff --git a/package.json b/package.json index 3dc8f69ec92..6f32a8f44d7 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "esbuild-plugin-wasm": "^1.1.0", "eslint": "^9.24.0", "eslint-config-prettier": "^10.1.2", + "eslint-plugin-check-file": "^3.3.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-license-header": "^0.8.0", "eslint-plugin-react": "^7.37.5",