From e93faf4caaf827adba4392356457633f67633bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Tue, 23 Jun 2026 19:22:54 +0200 Subject: [PATCH] Enforce Relay module-name prefix in naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relay 21 made the filename-prefix requirement opt-in for non-Haste projects, and the relay/graphql-naming lint rule only covers operations and legacy fragment containers, leaving hooks-based fragment names unguarded. Re-enable compiler enforcement via enforce_module_name_prefix_for_non_haste so fragments keep the collision-free, navigable _ convention, and document the rationale in the Relay guide. The compiler accepts the current sources unchanged, so existing names already comply. Signed-off-by: Émile Ré --- contrib/claude/relay.md | 24 ++++++++++++++++++++++-- relay.config.json | 3 ++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/contrib/claude/relay.md b/contrib/claude/relay.md index 29a87d659..c7e7ecd7c 100644 --- a/contrib/claude/relay.md +++ b/contrib/claude/relay.md @@ -25,6 +25,26 @@ make relay # merge split schemas + clean + compile Custom scalar mappings: `Datetime → string`, `GID → string`, `CursorKey → string`, `Duration → string`, `BigInt → number`, `EmailAddr → string`. +## Naming operations and fragments + +Every operation (query, mutation, subscription) and fragment carries its **module-name prefix**: `` for operations, `_` for fragments, where `` is the file's basename. + +```tsx +// PosterHovercard.tsx +query PosterHovercardQuery { ... } // operation: +fragment PosterHovercard_poster on Poster { ... } // fragment: _ +``` + +Relay 21 dropped the **compiler** requirement to prefix names with the filename for non-Haste projects (now opt-in via `enforce_module_name_prefix_for_non_haste`). We keep the prefix as mandatory house style: + +- **Operations still require it regardless** — the `relay/graphql-naming` rule (from `eslint-plugin-relay`'s `ts-recommended`) reports any operation whose name doesn't start with the module name. Dropping it only for fragments would split the convention. +- **Uniqueness** — Relay still requires globally-unique operation/fragment names per project; the module prefix is the collision-free scheme that guarantees it. +- **Discoverability** — generated artifact filenames and the `$key` / `$data` types derive from the name, so `PosterHovercard_poster$key` points straight back to its source module. + +We set `enforce_module_name_prefix_for_non_haste` in `relay.config.json` so the **compiler** guarantees the convention for fragments too — the lint rule only covers operations and legacy fragment containers, not hooks-based fragments. + +`` is the data the key feeds (the prop minus its `Key` suffix), never a redundant `Fragment` word: a `contactKey` prop reads a `ContactListItem_contact` fragment. + ## Colocated queries Queries are defined inline in the file that uses them. Route-level queries are preloaded in a dedicated `*PageLoader` component before the page renders. @@ -191,7 +211,7 @@ Fragments colocate data requirements with the component that reads them: ```tsx const contactFragment = graphql` - fragment ContactListItem_contactFragment on ThirdPartyContact { + fragment ContactListItem_contact on ThirdPartyContact { id fullName email @@ -204,7 +224,7 @@ const contactFragment = graphql` } `; -function ContactListItem(props: { contactKey: ContactListItem_contactFragment$key }) { +function ContactListItem(props: { contactKey: ContactListItem_contact$key }) { const contact = useFragment(contactFragment, props.contactKey); // ... } diff --git a/relay.config.json b/relay.config.json index 999d0d535..618c408d9 100644 --- a/relay.config.json +++ b/relay.config.json @@ -1,7 +1,8 @@ { "root": ".", "featureFlags": { - "enforce_fragment_alias_where_ambiguous": { "kind": "disabled" } + "enforce_fragment_alias_where_ambiguous": { "kind": "disabled" }, + "enforce_module_name_prefix_for_non_haste": true }, "sources": { "apps/console/src/pages/iam": "iam",