Files
vibe-kanban/frontend/.eslintrc.cjs
Louis Knight-Webb 36bde0b4b9 Frontend file naming eslint rule (vibe-kanban) (#1322)
* Perfect! Now let me create a summary for you:

##  ESLint Configuration Complete

The linter has identified **29 files** that need to be renamed:

### **Files to Rename:**

#### **Components → PascalCase (15 files)**
```
config-provider.tsx → ConfigProvider.tsx
diff-view-switch.tsx → DiffViewSwitch.tsx
logo.tsx → Logo.tsx
navbar.tsx → Navbar.tsx (in layout/)
search-bar.tsx → SearchBar.tsx
theme-provider.tsx → ThemeProvider.tsx
style-override.tsx → StyleOverride.tsx

copy-files-field.tsx → CopyFilesField.tsx (in projects/)
project-detail.tsx → ProjectDetail.tsx (in projects/)
project-form-fields.tsx → ProjectFormFields.tsx (in projects/)
project-list.tsx → ProjectList.tsx (in projects/)
```

#### **Contexts → PascalCase (2 files)**
```
project-context.tsx → ProjectContext.tsx
search-context.tsx → SearchContext.tsx
```

#### **Pages → PascalCase (3 files)**
```
full-attempt-logs.tsx → FullAttemptLogs.tsx
project-tasks.tsx → ProjectTasks.tsx
projects.tsx → Projects.tsx
```

#### **Lib → camelCase (2 files)**
```
caret-position.ts → caretPosition.ts
mcp-strategies.ts → mcpStrategies.ts
```

#### **Utils → camelCase (3 files)**
```
companion-install-task.ts → companionInstallTask.ts
script-placeholders.ts → scriptPlaceholders.ts
status-labels.ts → statusLabels.ts
```

#### **UI (shadcn) → kebab-case (4 files)**
```
ActionsDropdown.tsx → actions-dropdown.tsx
ImageUploadSection.tsx → image-upload-section.tsx
TitleDescriptionEditor.tsx → title-description-editor.tsx
DataTable.tsx → data-table.tsx (in table/)
Table.tsx → table.tsx (in table/)
ImageChipNode.tsx → image-chip-node.tsx (in wysiwyg/)
imageChipMarkdown.ts → image-chip-markdown.ts (in wysiwyg/)
```

#### **Entry Point (special case - 1 file)**
```
main.tsx → can stay as main.tsx (entry point convention)
```

**Next steps:** Should I proceed with renaming these files using `git mv`?

* ##  Complete! File Naming Standardization Successful

### **Summary**

Successfully implemented and enforced file naming conventions for the frontend:

### **What Was Done:**

1.  **Installed** `eslint-plugin-check-file@^2.8.0`

2.  **Configured ESLint** with file naming rules:
   - **Components (.tsx)**: `PascalCase`
   - **Hooks (.ts)**: `camelCase` (with `use` prefix)
   - **Utils/Lib (.ts)**: `camelCase`
   - **Shadcn UI**: `kebab-case` (library convention)
   - **Entry points**: Excluded (`main.tsx`, `vite-env.d.ts`)

3.  **Renamed 28 files** using `git mv`:
   - 10 components → PascalCase
   - 2 contexts → PascalCase
   - 3 pages → PascalCase
   - 2 lib files → camelCase
   - 3 utils → camelCase
   - 7 UI components → kebab-case
   - 1 utils component → PascalCase

4.  **Fixed all imports** across the codebase (77 files modified)

5.  **Verified**:
   -  ESLint passes (no filename errors)
   -  TypeScript check passes (no errors)
   -  Build successful

### **Git Status:**
- 28 files renamed (with history preserved via `git mv`)
- 77 total files changed (renames + import updates)

### **Ready for commit!**

The codebase now has consistent, enforceable file naming conventions that will prevent future inconsistencies.

* Cleanup script changes for task attempt 11f8d7b9-99b5-41ce-934c-5ce75008a200
2025-11-18 17:05:18 +00:00

180 lines
5.2 KiB
JavaScript

const i18nCheck = process.env.LINT_I18N === 'true';
module.exports = {
root: true,
env: {
browser: true,
es2020: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:i18next/recommended',
'plugin:eslint-comments/recommended',
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh', '@typescript-eslint', 'unused-imports', 'i18next', 'eslint-comments', 'check-file'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
},
rules: {
'eslint-comments/no-use': ['error', { allow: [] }],
'react-refresh/only-export-components': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'error',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: false,
},
],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/switch-exhaustiveness-check': 'error',
// Enforce typesafe modal pattern
'no-restricted-imports': [
'error',
{
paths: [
{
name: '@ebay/nice-modal-react',
importNames: ['default'],
message:
'Import NiceModal only in lib/modals.ts or dialog component files. Use DialogName.show(props) instead.',
},
{
name: '@/lib/modals',
importNames: ['showModal', 'hideModal', 'removeModal'],
message:
'Do not import showModal/hideModal/removeModal. Use DialogName.show(props) and DialogName.hide() instead.',
},
],
},
],
'no-restricted-syntax': [
'error',
{
selector:
'CallExpression[callee.object.name="NiceModal"][callee.property.name="show"]',
message:
'Do not use NiceModal.show() directly. Use DialogName.show(props) instead.',
},
{
selector:
'CallExpression[callee.object.name="NiceModal"][callee.property.name="register"]',
message:
'Do not use NiceModal.register(). Dialogs are registered automatically.',
},
{
selector: 'CallExpression[callee.name="showModal"]',
message:
'Do not use showModal(). Use DialogName.show(props) instead.',
},
{
selector: 'CallExpression[callee.name="hideModal"]',
message: 'Do not use hideModal(). Use DialogName.hide() instead.',
},
{
selector: 'CallExpression[callee.name="removeModal"]',
message: 'Do not use removeModal(). Use DialogName.remove() instead.',
},
],
// i18n rule - only active when LINT_I18N=true
'i18next/no-literal-string': i18nCheck
? [
'warn',
{
markupOnly: true,
ignoreAttribute: [
'data-testid',
'to',
'href',
'id',
'key',
'type',
'role',
'className',
'style',
'aria-describedby',
],
'jsx-components': {
exclude: ['code'],
},
},
]
: 'off',
// File naming conventions
'check-file/filename-naming-convention': [
'error',
{
// React components (tsx) should be PascalCase
'src/**/*.tsx': 'PASCAL_CASE',
// Hooks should be camelCase starting with 'use'
'src/**/use*.ts': 'CAMEL_CASE',
// Utils should be camelCase
'src/utils/**/*.ts': 'CAMEL_CASE',
// Lib/config/constants should be camelCase
'src/lib/**/*.ts': 'CAMEL_CASE',
'src/config/**/*.ts': 'CAMEL_CASE',
'src/constants/**/*.ts': 'CAMEL_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
},
overrides: [
{
// Entry point exception - main.tsx can stay lowercase
files: ['src/main.tsx', 'src/vite-env.d.ts'],
rules: {
'check-file/filename-naming-convention': 'off',
},
},
{
// Shadcn UI components are an exception - keep kebab-case
files: ['src/components/ui/**/*.{ts,tsx}'],
rules: {
'check-file/filename-naming-convention': [
'error',
{
'src/components/ui/**/*.{ts,tsx}': 'KEBAB_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
},
},
{
files: ['**/*.test.{ts,tsx}', '**/*.stories.{ts,tsx}'],
rules: {
'i18next/no-literal-string': 'off',
},
},
{
// Disable type-aware linting for config files
files: ['*.config.{ts,js,cjs,mjs}', '.eslintrc.cjs'],
parserOptions: {
project: null,
},
rules: {
'@typescript-eslint/switch-exhaustiveness-check': 'off',
},
},
{
// Allow NiceModal usage in lib/modals.ts, App.tsx (for Provider), and dialog component files
files: ['src/lib/modals.ts', 'src/App.tsx', 'src/components/dialogs/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': 'off',
'no-restricted-syntax': 'off',
},
},
],
};