✨ feat(eslint): enforce PascalCase use-case file naming#352
Conversation
New workspace ESLint plugin (eslint/) with a use-case-filename rule that bans the legacy <name>.usecase.ts form and requires PascalCase. Unit-tested with RuleTester. Not yet wired into the root config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename all <name>.usecase.ts use-case files (and their specs) to the PascalCase <Name>UseCase.ts convention, updating imports and barrels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the packmind/use-case-filename rule into the root flat config for **/application/useCases/**/*.ts so npm run lint fails on the legacy <name>.usecase.ts form. All existing files already renamed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Allow require() in the CommonJS rule/plugin files so the inferred eslint-rules:lint target passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Too many files changed for review. ( |
Rename CommitToGit, HandleWebHook, HandleWebHookWithoutContent and GetFileFromRepo (classes + files + references) to end in UseCase, for a consistent use-case naming convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename use-case classes (and their files + references) from the inconsistent "...Usecase" form to PascalCase "...UseCase". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename use-case classes (and their files + references) from the inconsistent "...Usecase" form to PascalCase "...UseCase". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename use-case classes (and their files + references) from the inconsistent "...Usecase" form to PascalCase "...UseCase". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename use-case classes (and their files + references) from the inconsistent "...Usecase" form to PascalCase "...UseCase". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upgrade packmind/use-case-filename to require PascalCase ending in
"UseCase" (capital C) for use-case files and exported classes:
- ban legacy ".usecase" filenames (preserving the original .ts/.tsx
extension in the suggestion) and mis-cased "Usecase" filenames
- flag exported, non-Error classes that are mis-cased ("Usecase") or
missing the "UseCase" suffix
- scope the rule to packages/ only; apps/* may co-locate non-use-case
classes under useCases/ (e.g. the CLI diff strategies)
Addresses review feedback: message no longer overpromises, .tsx is
preserved, and Usecase/UseCase casing is unified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review feedback addressedResolved the three findings by tightening the convention to #1 — message contradicted the applied convention. The rule now positively enforces the convention via AST, so the message is true:
The 4 bare git classes ( #2 — #3 — Scope note: the rule runs only on Verified: |
Sweep the mis-cased "Usecase" -> "UseCase" across instance variables, the LinterUseCases aggregator (class + file), and stale log/test labels, so the codebase is consistent with the use-case naming convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the use-case naming checks into two rules: - use-case-filename: structural (legacy .usecase ban + bare class must end in UseCase), scoped to packages useCases/. - usecase-casing: repo-wide, flags the mis-cased "Usecase" in filenames AND identifiers (class names, variables, parameters, properties, types), so instance variables and aggregators stay consistent too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add {workspaceRoot}/eslint/**/* to the `default` namedInput so editing the
custom lint rules invalidates the Nx lint cache. Without this, a change to a
rule's logic would return stale cached lint results (the inferred lint inputs
only track eslint.config.mjs and tools/eslint-rules/**/*), so `npm run lint`
and the pre-push `nx affected -t lint` could miss it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The classMissingSuffix message only said to rename to *UseCase, leaving the shared/ exemption (already configured in the root flat config) for devs to discover. A legitimate helper class (mapper/DTO/builder) exported under useCases/ would error with no obvious exit. Narrowing detection by inheritance was rejected: many real use cases extend no base class, so gating on `extends *UseCase` would silently miss them. Instead the message now names both exits — rename to *UseCase, or move helpers under shared/ — keeping detection coverage unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new workspace-rules project lived in a top-level `eslint/` directory.
That name shadows the `eslint` npm package under Nx's config loader: when
`@nx/eslint/plugin` processes `eslint.config.mjs`, `@typescript-eslint/utils`
runs `require("eslint").ESLint.version` at module init and resolves to our
`eslint/index.js` (which exports `{ rules }`, no `ESLint`), throwing
"Cannot read properties of undefined (reading 'version')".
Project graph construction then fails, so every `nx run` app refuses to
boot. CI runs with `NX_DAEMON=false` (fresh graph each time) so it fails
hard; locally the daemon cache masked it. This is why the e2e environment
never came up and the e2e job timed out.
Rename the directory to `eslint-rules` (matching the Nx project name) and
update the three external references: the root config import, the nx.json
`default` named input glob, and project.json `sourceRoot`.
|



Explanation
Enforce the use-case file naming convention automatically. Use-case files under
packages/**/application/useCases/followed two conflicting conventions — thedesired PascalCase form (
AddGitRepoUseCase.ts) and a legacy lowercase-suffixform (
addGitRepo.usecase.ts).This PR:
packmind/use-case-filename(neweslint/plugin) that bans the legacy
.usecasesuffix and requires PascalCase. It isunit-tested with
RuleTesterand wired into the root flat config aserror,so
npm run lintnow fails on any*.usecase.tsfile.packages to PascalCase, where each file name matches its exported class name.
Type of Change
Affected Components
llm,recipes,deployments,skills,git,standards(file renames); neweslint-rulestooling projectTesting
Test Details:
RuleTestersuite forpackmind/use-case-filename(9 cases: PascalCase + helpers likeindex.ts/utils.ts/shared/*valid;*.usecase.ts/*.usecase.spec.tsinvalid; one case locks the exact error message wording).npm run lint→ 31 projects, 0 errorsnpm run build→ 27 projectsnpm run test→ 26 projects (exit 0)tmpProbe.usecase.tstriggers the rule with a clear, actionable error suggestingTmpProbeUseCase.ts, then removed.TODO List
Reviewer Notes
.usecasesuffix (not a positive PascalCaserequirement), so helper files inside
useCases/are never false-positives.don't end in
UseCase(e.g.commitToGit.usecase.ts→CommitToGit.ts).filesglob is project-relative (**/application/useCases/**/*.ts)because
nx lintrunseslint .with cwd = each project dir.eslint/eslint.config.mjsallows
require()in those files so the inferredeslint-rules:linttarget passes.