Changed ghost/core to ESLint 9 / flat config#28807
Conversation
ref https://linear.app/tryghost/ - migrated ghost/core/.eslintrc.js + ghost/core/test/.eslintrc.js to a single eslint.config.mjs; folded the two .eslintignore files into flat-config 'ignores' (with the !member-attribution allow-back preserved) - replaced 'ghost/filenames/match-regex' with a local inline rule that does the same filename check (no scope traversal); the upstream rule's context.getScope() call is broken under ESLint 9 - preserved every legacy override: API endpoints, migrations (filenames + no-loops + no-return-in-loop), schema.js (no created_by/updated_by), core/** kebab-case filenames (adapters carve-out), frontend helpers underscores, admin-auth + member-attribution browser env, shared/frontend/ server require allowlists - pinned 'no-unused-vars' caughtErrors: 'none' and disabled 'no-unused-private-class-members' (ESLint 9 added this to recommended) - relaxed typescript-eslint v8's stricter recommended rules (no-require-imports, no-unused-expressions, no-unsafe-function-type, ban-ts-comment) to match the legacy plugin:ghost/ts posture; will tighten later — supersedes the temporary workaround in PR #28804 which disabled no-unused-expressions in the legacy .eslintrc.js - removed --ext (ESLint 9 dropped it) and --ignore-path flags from lint scripts - updated three eslint-disable comments that referenced ghost/filenames/match-regex to suppress the new local-filenames/match-regex rule (automated_emails.js, password_reset.js, automation_email_previews.js) - tests run a mix of mocha + vitest; mocha rules off by default with the one legacy 'ghost/mocha/no-skipped-tests' kept on
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThe Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run @tryghost/admin-x-settings:test:acceptance |
✅ Succeeded | 9m 48s | View ↗ |
nx build @tryghost/announcement-bar |
✅ Succeeded | <1s | View ↗ |
nx build @tryghost/sodo-search |
✅ Succeeded | 1s | View ↗ |
nx build @tryghost/activitypub |
✅ Succeeded | 2s | View ↗ |
nx build @tryghost/signup-form |
✅ Succeeded | <1s | View ↗ |
nx build @tryghost/comments-ui |
✅ Succeeded | <1s | View ↗ |
nx build @tryghost/admin-toolbar |
✅ Succeeded | <1s | View ↗ |
nx build @tryghost/portal |
✅ Succeeded | <1s | View ↗ |
Additional runs (15) |
✅ Succeeded | ... | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-06-22 22:29:13 UTC
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ghost/core/eslint.config.mjs`:
- Around line 193-196: The glob pattern in the max-api-complexity rule
configuration at the target location only matches top-level files in the
endpoints directory and excludes nested utility files in subdirectories. Either
update the glob pattern from the current single-level wildcard to a recursive
pattern to include nested endpoint files, or add a comment documenting why
utilities in nested subdirectories are intentionally excluded from the
complexity rule. This will clarify the design decision and prevent
inconsistencies if endpoint files are later organized in subdirectories.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 11f519b7-0bac-42cb-a77f-cc493c3723b6
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
ghost/core/.eslintignoreghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/utils/validators/input/automated_emails.jsghost/core/core/server/api/endpoints/utils/validators/input/automation_email_previews.jsghost/core/core/server/api/endpoints/utils/validators/input/password_reset.jsghost/core/eslint.config.mjsghost/core/package.jsonghost/core/test/.eslintignoreghost/core/test/.eslintrc.js
💤 Files with no reviewable changes (4)
- ghost/core/test/.eslintignore
- ghost/core/test/.eslintrc.js
- ghost/core/.eslintrc.js
- ghost/core/.eslintignore
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #28807 +/- ##
==========================================
+ Coverage 74.07% 74.10% +0.02%
==========================================
Files 1560 1560
Lines 134826 134826
Branches 16316 16328 +12
==========================================
+ Hits 99875 99915 +40
+ Misses 33941 33931 -10
+ Partials 1010 980 -30
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|

Summary
Migrates ghost/core — the backend — from
.eslintrc.jsto flat config. Largest of the per-workspace migrations because of the number of path-based overrides.ghost/core/.eslintrc.js+ghost/core/test/.eslintrc.jswith a singleeslint.config.mjs. Two.eslintignorefiles folded into flat-configignores(with the!member-attributionallow-back preserved as a negation entry).ghost/filenames/match-regexwith a small inlinelocal-filenames/match-regexrule. The upstream rule'sgetExportedClasswalks the scope chain via the removedcontext.getScope()API — that path triggers onmodule.exports = <Identifier>source, which is everywhere inghost/core. The inline replacement does just the filename check (noignoreExporting/ignoreExportingClass).max-api-complexity1.*/2.*/3.*exclusions),no-restricted-syntaxagainst loops + knex multi-join,ghost/no-return-in-loop/no-return-in-loopcore/server/data/schema/schema.js—created_by/updated_bybancore/**/*.{js,ts}— kebab-case filenames (adapters carve-out)core/frontend/helpers/**+core/frontend/apps/*/lib/helpers/**— underscores allowed (Handlebars helper names)core/frontend/src/admin-auth/**— browser env,no-consoleoff, native errors allowedcore/frontend/src/member-attribution/**— the one frontend/src/ subtree that's lint-scoped, browser env, ESM sourcecore/frontend/**andcore/server/**require allowlists (server↔frontend boundary)'no-unused-vars'caughtErrors: 'none'and disabled'no-unused-private-class-members'(added toeslint:recommendedin ESLint 9).no-require-imports,no-unused-expressions,no-unsafe-function-type,ban-ts-comment) to match the legacyplugin:ghost/tsposture. Theno-unused-expressionsrelaxation supersedes the temporary workaround in #28804, which disabled the rule in the legacy.eslintrc.jsbecause the @typescript-eslint v8.49 plugin's rule wrapper was misbehaving under ESLint 8.eslint-disable ghost/filenames/match-regexcomments to reference the newlocal-filenames/match-regexrule:core/server/api/endpoints/utils/validators/input/automated_emails.jscore/server/api/endpoints/utils/validators/input/password_reset.jscore/server/api/endpoints/utils/validators/input/automation_email_previews.js--extand the--ignore-pathflag; per-domainlint:server/lint:shared/lint:frontend/lint:test/lint:typesscripts pass directory args and let the config block'sfilesglob do the matching.core/serveralone; converting is its own roadmap.ghost/mocha/*rules disabled by default in the test block with the one legacy'ghost/mocha/no-skipped-tests': 'error'kept on.Test plan