@@ -30,28 +30,59 @@ func init() { Register() }
3030
3131// Generate produces the documentation JSON file.
3232func Generate (_ string , roots []eval.Root , files []* codegen.File ) ([]* codegen.File , error ) {
33+ // First, build a complete map of all definitions from all roots.
34+ // This ensures that cross-package type references can be resolved.
35+ allDefs := make (map [string ]* openapi.Schema )
3336 for _ , root := range roots {
3437 if r , ok := root .(* expr.RootExpr ); ok {
35- files = append (files , docsFile (r ))
38+ // Create a temporary, isolated context for each root to avoid global state pollution.
39+ prev := openapi .Definitions
40+ openapi .Definitions = make (map [string ]* openapi.Schema )
41+
42+ for _ , tpe := range r .Types {
43+ if ut , ok := tpe .(* expr.UserTypeExpr ); ok {
44+ openapi .GenerateTypeDefinition (r .API , ut )
45+ }
46+ }
47+ for _ , rt := range r .ResultTypes {
48+ openapi .GenerateResultTypeDefinition (r .API , rt , expr .DefaultView )
49+ }
50+
51+ // Merge the generated definitions into the global map.
52+ for n , s := range openapi .Definitions {
53+ if _ , exists := allDefs [n ]; ! exists {
54+ allDefs [n ] = dupSchema (s )
55+ }
56+ }
57+
58+ // Restore the original global definitions to maintain isolation.
59+ openapi .Definitions = prev
60+ }
61+ }
62+
63+ for _ , root := range roots {
64+ if r , ok := root .(* expr.RootExpr ); ok {
65+ files = append (files , docsFile (r , allDefs ))
3666 }
3767 }
3868 return files , nil
3969}
4070
41- func docsFile (r * expr.RootExpr ) * codegen.File {
42-
71+ func docsFile (r * expr.RootExpr , allDefs map [string ]* openapi.Schema ) * codegen.File {
4372 docs := & data {
4473 API : apiDocs (r .API ),
4574 Services : servicesDocs (r ),
4675 }
4776
4877 // Default behavior: use global OpenAPI definitions to preserve ordering and
4978 // compatibility with existing golden tests.
50- defs := openapi . Definitions
79+ defs := allDefs
5180
5281 // If either option is enabled, build a local definition map for this root
5382 // and apply transforms/inlining as needed, isolating from global state.
54- if plugexpr .Root .UseJSONTags || plugexpr .Root .InlineRefs {
83+ if plugexpr .Root .UseJSONTags {
84+ // Re-scope the definitions to only those present in the current root,
85+ // but use the globally-aware `allDefs` for lookups during transforms.
5586 local := make (map [string ]* openapi.Schema )
5687 prev := openapi .Definitions
5788 openapi .Definitions = make (map [string ]* openapi.Schema )
@@ -63,8 +94,10 @@ func docsFile(r *expr.RootExpr) *codegen.File {
6394 for _ , rt := range r .ResultTypes {
6495 openapi .GenerateResultTypeDefinition (r .API , rt , expr .DefaultView )
6596 }
66- for n , s := range openapi .Definitions {
67- local [n ] = dupSchema (s )
97+ for n := range openapi .Definitions {
98+ if def , ok := allDefs [n ]; ok {
99+ local [n ] = dupSchema (def )
100+ }
68101 }
69102 openapi .Definitions = prev
70103
@@ -92,31 +125,38 @@ func docsFile(r *expr.RootExpr) *codegen.File {
92125
93126 // Inline $refs if requested via DSL flag.
94127 if plugexpr .Root .InlineRefs {
128+ // When inlining, use the complete set of definitions from all roots
129+ // to ensure cross-package references can be resolved.
130+ inliningDefs := allDefs
131+ if plugexpr .Root .UseJSONTags {
132+ inliningDefs = transformDefinitionsWithJSONTagsHybrid (r , allDefs , nil )
133+ }
134+
95135 // Inline inside service payloads/results/errors.
96136 for _ , svc := range docs .Services {
97137 for _ , m := range svc .Methods {
98138 if m .Payload != nil && m .Payload .Type != nil {
99- inlineRefsInSchema (m .Payload .Type , defs , make (map [string ]bool ))
139+ inlineRefsInSchema (m .Payload .Type , inliningDefs , make (map [string ]bool ))
100140 }
101141 if m .StreamingPayload != nil && m .StreamingPayload .Type != nil {
102- inlineRefsInSchema (m .StreamingPayload .Type , defs , make (map [string ]bool ))
142+ inlineRefsInSchema (m .StreamingPayload .Type , inliningDefs , make (map [string ]bool ))
103143 }
104144 if m .Result != nil && m .Result .Type != nil {
105- inlineRefsInSchema (m .Result .Type , defs , make (map [string ]bool ))
145+ inlineRefsInSchema (m .Result .Type , inliningDefs , make (map [string ]bool ))
106146 }
107147 if m .StreamingResult != nil && m .StreamingResult .Type != nil {
108- inlineRefsInSchema (m .StreamingResult .Type , defs , make (map [string ]bool ))
148+ inlineRefsInSchema (m .StreamingResult .Type , inliningDefs , make (map [string ]bool ))
109149 }
110150 for _ , e := range m .Errors {
111151 if e != nil && e .Type != nil {
112- inlineRefsInSchema (e .Type , defs , make (map [string ]bool ))
152+ inlineRefsInSchema (e .Type , inliningDefs , make (map [string ]bool ))
113153 }
114154 }
115155 }
116156 }
117157 // Inline inside definitions themselves (properties that refer to other defs).
118158 for _ , def := range defs {
119- inlineRefsInSchema (def , defs , make (map [string ]bool ))
159+ inlineRefsInSchema (def , inliningDefs , make (map [string ]bool ))
120160 }
121161 }
122162
0 commit comments