1- import type { Program , Type } from "@typespec/compiler" ;
2- import { useStateSet } from "@typespec/compiler/utils" ;
3- import { GraphQLKeys } from "../lib.js" ;
1+ import type {
2+ DecoratedType ,
3+ DecoratorContext ,
4+ DecoratorFunction ,
5+ Model ,
6+ ModelProperty ,
7+ Operation ,
8+ Type ,
9+ Union ,
10+ } from "@typespec/compiler" ;
11+ import { NAMESPACE } from "../lib.js" ;
412
5- const [ getNullableState , setNullableState ] = useStateSet < Type > ( GraphQLKeys . nullable ) ;
13+ // This will set the namespace for decorators implemented in this file
14+ export const namespace = NAMESPACE ;
15+
16+ /**
17+ * Decorator implementation for `@nullable`.
18+ *
19+ * No-op — the decorator's presence on the type's `decorators` array is the
20+ * signal. No additional state storage is needed.
21+ */
22+ export const $nullable : DecoratorFunction = (
23+ _context : DecoratorContext ,
24+ _target : ModelProperty | Operation | Union | Model ,
25+ ) => { } ;
26+
27+ /**
28+ * Decorator implementation for `@nullableElements`.
29+ *
30+ * No-op — presence on the decorators array is the signal.
31+ */
32+ export const $nullableElements : DecoratorFunction = (
33+ _context : DecoratorContext ,
34+ _target : ModelProperty | Operation ,
35+ ) => { } ;
636
737/**
838 * Check whether a type was marked nullable after null-variant stripping.
@@ -12,30 +42,39 @@ const [getNullableState, setNullableState] = useStateSet<Type>(GraphQLKeys.nulla
1242 * - **Operation**: return type `T | null`
1343 * - **Union**: named unions like `Cat | Dog | null` (safe — new unique object)
1444 */
15- export function isNullable ( program : Program , type : Type ) : boolean {
16- return getNullableState ( program , type ) ;
45+ export function isNullable ( type : Type ) : boolean {
46+ if ( ! isDecoratedType ( type ) ) return false ;
47+ return type . decorators . some ( ( d ) => d . decorator === $nullable ) ;
1748}
1849
19- /** Mark a type, property, or operation as nullable. */
20- export function setNullable ( program : Program , type : Type ) : void {
21- setNullableState ( program , type ) ;
50+ /**
51+ * Mark a type, property, or operation as nullable.
52+ * Called by the mutation engine when null variants are stripped.
53+ */
54+ export function setNullable ( type : Type ) : void {
55+ if ( ! isDecoratedType ( type ) ) return ;
56+ if ( type . decorators . some ( ( d ) => d . decorator === $nullable ) ) return ;
57+ type . decorators . push ( { decorator : $nullable , args : [ ] } ) ;
2258}
2359
24- const [ getNullableElementsState , setNullableElementsState ] = useStateSet < Type > (
25- GraphQLKeys . nullableElements ,
26- ) ;
27-
2860/**
2961 * Check whether a property's array elements were originally `T | null`.
3062 *
3163 * For `(string | null)[]`, marks the ModelProperty so components emit
3264 * `[String]` instead of `[String!]`.
3365 */
34- export function hasNullableElements ( program : Program , type : Type ) : boolean {
35- return getNullableElementsState ( program , type ) ;
66+ export function hasNullableElements ( type : Type ) : boolean {
67+ if ( ! isDecoratedType ( type ) ) return false ;
68+ return type . decorators . some ( ( d ) => d . decorator === $nullableElements ) ;
3669}
3770
3871/** Mark a property as having nullable array elements. */
39- export function setNullableElements ( program : Program , type : Type ) : void {
40- setNullableElementsState ( program , type ) ;
72+ export function setNullableElements ( type : Type ) : void {
73+ if ( ! isDecoratedType ( type ) ) return ;
74+ if ( type . decorators . some ( ( d ) => d . decorator === $nullableElements ) ) return ;
75+ type . decorators . push ( { decorator : $nullableElements , args : [ ] } ) ;
76+ }
77+
78+ function isDecoratedType ( type : Type ) : type is Type & DecoratedType {
79+ return "decorators" in type ;
4180}
0 commit comments