Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
fix(@nestjs/graphql): preserve shared ref when no parent directives
When a target has no parent class directives to inherit, return the same classDirectives array reference rather than a fresh copy. compile() mutates the per-target array in place via reverse(), and item.directives needs to stay aligned with that mutation across repeated compile invocations. Only build a new array when combining directives from an abstract parent. Fixes federation e2e regression where User directives were emitted in the wrong order.
  • Loading branch information
maruthang committed Apr 20, 2026
commit 559369f409f195bd4753a8e6b090d87418aa7fed
Original file line number Diff line number Diff line change
Expand Up @@ -298,26 +298,35 @@ export class TypeMetadataStorageHost {
private getInheritedClassDirectives(
target: Function,
): ClassDirectiveMetadata[] {
const directives: ClassDirectiveMetadata[] = [];
const seenSdls = new Set<string>();
const ownDirectives = this.metadataByTargetCollection
.get(target)
.classDirectives.getAll();

let current: Function | null = target;
const inherited: ClassDirectiveMetadata[] = [];
const seenSdls = new Set<string>(ownDirectives.map((d) => d.sdl));

let current: Function | null = Object.getPrototypeOf(target);
while (current && current !== Function.prototype) {
const targetDirectives = this.metadataByTargetCollection
this.metadataByTargetCollection
.get(current)
.classDirectives.getAll();

targetDirectives.forEach((directive) => {
if (!seenSdls.has(directive.sdl)) {
seenSdls.add(directive.sdl);
directives.push(directive);
}
});

.classDirectives.getAll()
.forEach((directive) => {
if (!seenSdls.has(directive.sdl)) {
seenSdls.add(directive.sdl);
inherited.push(directive);
}
});
current = Object.getPrototypeOf(current);
}

return directives;
// Preserve the shared-reference semantic when nothing is inherited: the
// compile() pass mutates the per-target `classDirectives` array in place
// (reverse() is called on it), and `item.directives` is expected to stay
// in sync with that mutation. Only produce a fresh array when we actually
// have to combine directives from an abstract parent.
return inherited.length === 0
? ownDirectives
: [...ownDirectives, ...inherited];
}

clear() {
Expand Down