@@ -4,12 +4,15 @@ package codegen
44import (
55 "fmt"
66 "strings"
7- "text/template"
87
98 "github.com/pb33f/libopenapi"
109 "github.com/pb33f/libopenapi/datamodel/high/base"
10+ "golang.org/x/tools/imports"
1111
12+ "github.com/oapi-codegen/oapi-codegen-exp/experimental/codegen/internal/dce"
13+ "github.com/oapi-codegen/oapi-codegen-exp/experimental/codegen/internal/runtimeextract"
1214 "github.com/oapi-codegen/oapi-codegen-exp/experimental/codegen/internal/templates"
15+ runtime "github.com/oapi-codegen/oapi-codegen-exp/experimental/codegen/internal/runtime"
1316)
1417
1518// Generate produces Go code from the parsed OpenAPI document.
@@ -150,8 +153,6 @@ func Generate(doc libopenapi.Document, specData []byte, cfg Configuration) (stri
150153 ctx .AddImportAlias (cfg .Generation .ModelsPackage .Path , cfg .Generation .ModelsPackage .Alias )
151154 }
152155
153- // Register form helper if any operation has form-encoded bodies
154- ctx .NeedFormHelper (ops )
155156 }
156157
157158 // Track whether shared error types have been generated to avoid duplication.
@@ -228,7 +229,6 @@ func Generate(doc libopenapi.Document, specData []byte, cfg Configuration) (stri
228229 ctx .AddImportAlias (cfg .Generation .ModelsPackage .Path , cfg .Generation .ModelsPackage .Alias )
229230 }
230231
231- ctx .NeedFormHelper (webhookOps )
232232 }
233233 }
234234
@@ -258,7 +258,6 @@ func Generate(doc libopenapi.Document, specData []byte, cfg Configuration) (stri
258258 ctx .AddImportAlias (cfg .Generation .ModelsPackage .Path , cfg .Generation .ModelsPackage .Alias )
259259 }
260260
261- ctx .NeedFormHelper (callbackOps )
262261 }
263262 }
264263
@@ -358,44 +357,23 @@ func Generate(doc libopenapi.Document, specData []byte, cfg Configuration) (stri
358357
359358 if cfg .Generation .RuntimePackage != nil {
360359 // Runtime package is configured — don't embed helpers, import them.
361- // Add imports for whichever sub-packages are actually needed.
360+ // Always add all three sub-package imports; the Go compiler and goimports
361+ // will strip any that end up unused.
362362 ctx .AddImportAlias (cfg .Generation .RuntimePackage .TypesImport (), "oapiCodegenTypesPkg" )
363- if ctx .HasAnyParams () {
364- ctx .AddImportAlias (cfg .Generation .RuntimePackage .ParamsImport (), "oapiCodegenParamsPkg" )
365- }
366- if len (ctx .RequiredHelpers ()) > 0 {
367- ctx .AddImportAlias (cfg .Generation .RuntimePackage .HelpersImport (), "oapiCodegenHelpersPkg" )
368- }
363+ ctx .AddImportAlias (cfg .Generation .RuntimePackage .ParamsImport (), "oapiCodegenParamsPkg" )
364+ ctx .AddImportAlias (cfg .Generation .RuntimePackage .HelpersImport (), "oapiCodegenHelpersPkg" )
369365 } else {
370- // Resolve param template dependencies first — this may register
371- // additional custom types (e.g., Date) that need to be emitted.
372- ctx .GetRequiredParamTemplates ()
373-
374- // Emit custom type templates (Date, Email, UUID, File, Nullable, etc.)
375- for _ , templateName := range ctx .RequiredCustomTypes () {
376- typeCode := ctx .loadAndRegisterCustomType (templateName )
377- if typeCode != "" {
378- output .AddType (typeCode )
379- }
380- }
381-
382- // Emit param functions (typesPrefix is empty when embedded — Date is in same package)
383- paramFuncs , err := generateParamFunctionsFromContext (ctx , "" )
366+ // Inline mode: emit all runtime code, DCE will remove unused declarations.
367+ runtimeCode , runtimeImports , err := runtimeextract .ExtractAllInline (runtime .SourceFS )
384368 if err != nil {
385- return "" , fmt .Errorf ("generating param functions: %w" , err )
386- }
387- if paramFuncs != "" {
388- output .AddType (paramFuncs )
369+ return "" , fmt .Errorf ("extracting runtime code: %w" , err )
389370 }
390-
391- // Emit helper templates (e.g., marshal_form)
392- for _ , helperName := range ctx .RequiredHelpers () {
393- helperCode , err := generateHelper (helperName , ctx )
394- if err != nil {
395- return "" , fmt .Errorf ("generating helper %s: %w" , helperName , err )
396- }
397- if helperCode != "" {
398- output .AddType (helperCode )
371+ output .AddType (runtimeCode )
372+ for _ , imp := range runtimeImports {
373+ if imp .Alias != "" {
374+ ctx .AddImportAlias (imp .Path , imp .Alias )
375+ } else {
376+ ctx .AddImport (imp .Path )
399377 }
400378 }
401379 }
@@ -404,101 +382,26 @@ func Generate(doc libopenapi.Document, specData []byte, cfg Configuration) (stri
404382 // Transfer all imports from ctx to output
405383 output .AddImports (ctx .Imports ())
406384
407- return output .Format ()
408- }
409-
410- // generateHelper generates a helper template by name and registers its imports on the context.
411- func generateHelper (name string , ctx * CodegenContext ) (string , error ) {
412- switch name {
413- case "marshal_form" :
414- ctx .AddTemplateImports (templates .MarshalFormHelperTemplate .Imports )
415- return generateMarshalFormHelper ()
416- case "json_merge" :
417- ctx .AddTemplateImports (templates .JSONMergeHelperTemplate .Imports )
418- return generateJSONMergeHelper ()
419- default :
420- return "" , fmt .Errorf ("unknown helper: %s" , name )
421- }
422- }
423-
424- // generateMarshalFormHelper generates the marshalForm helper function.
425- func generateMarshalFormHelper () (string , error ) {
426- tmplInfo := templates .MarshalFormHelperTemplate
427- content , err := templates .TemplateFS .ReadFile ("files/" + tmplInfo .Template )
428- if err != nil {
429- return "" , fmt .Errorf ("reading form helper template: %w" , err )
430- }
431-
432- tmpl , err := template .New (tmplInfo .Name ).Parse (string (content ))
433- if err != nil {
434- return "" , fmt .Errorf ("parsing form helper template: %w" , err )
435- }
436-
437- var result strings.Builder
438- if err := tmpl .Execute (& result , nil ); err != nil {
439- return "" , fmt .Errorf ("executing form helper template: %w" , err )
440- }
441-
442- return result .String (), nil
443- }
444-
445- // generateJSONMergeHelper generates the JSONMerge helper function.
446- func generateJSONMergeHelper () (string , error ) {
447- tmplInfo := templates .JSONMergeHelperTemplate
448- content , err := templates .TemplateFS .ReadFile ("files/" + tmplInfo .Template )
385+ formatted , err := output .Format ()
449386 if err != nil {
450- return "" , fmt . Errorf ( "reading json merge helper template: %w" , err )
387+ return "" , err
451388 }
452389
453- tmpl , err := template .New (tmplInfo .Name ).Parse (string (content ))
454- if err != nil {
455- return "" , fmt .Errorf ("parsing json merge helper template: %w" , err )
456- }
457-
458- var result strings.Builder
459- if err := tmpl .Execute (& result , nil ); err != nil {
460- return "" , fmt .Errorf ("executing json merge helper template: %w" , err )
461- }
462-
463- return result .String (), nil
464- }
465-
466- // generateParamFunctionsFromContext generates the parameter styling/binding functions based on CodegenContext usage.
467- // typesPrefix is prepended to Date/DateFormat references in param templates; empty when embedded.
468- func generateParamFunctionsFromContext (ctx * CodegenContext , typesPrefix string ) (string , error ) {
469- if ! ctx .HasAnyParams () {
470- return "" , nil
471- }
472-
473- var result strings.Builder
474-
475- requiredTemplates := ctx .GetRequiredParamTemplates ()
476-
477- for _ , tmplInfo := range requiredTemplates {
478- content , err := templates .TemplateFS .ReadFile ("files/" + tmplInfo .Template )
390+ // Run DCE to remove unused runtime declarations (only relevant in inline mode).
391+ if cfg .Generation .RuntimePackage == nil {
392+ formatted , err = dce .EliminateDeadCode (formatted )
479393 if err != nil {
480- return "" , fmt .Errorf ("reading param template %s : %w" , tmplInfo . Template , err )
394+ return "" , fmt .Errorf ("dead code elimination : %w" , err )
481395 }
482-
483- tmpl , err := template .New (tmplInfo .Name ).Funcs (template.FuncMap {
484- "typesPrefix" : func () string { return typesPrefix },
485- }).Parse (string (content ))
396+ // DCE re-prints via go/printer which changes formatting; normalize with goimports.
397+ reformatted , err := imports .Process ("" , []byte (formatted ), nil )
486398 if err != nil {
487- return "" , fmt .Errorf ("parsing param template %s: %w" , tmplInfo .Template , err )
488- }
489-
490- if err := tmpl .Execute (& result , nil ); err != nil {
491- return "" , fmt .Errorf ("executing param template %s: %w" , tmplInfo .Template , err )
399+ return "" , fmt .Errorf ("formatting after DCE: %w" , err )
492400 }
493- result .WriteString ("\n " )
494- }
495-
496- // Register param imports on the context
497- for _ , imp := range ctx .GetRequiredParamImports () {
498- ctx .AddImportAlias (imp .Path , imp .Alias )
401+ formatted = string (reformatted )
499402 }
500403
501- return result . String () , nil
404+ return formatted , nil
502405}
503406
504407// generateType generates Go code for a single schema descriptor.
@@ -1072,7 +975,6 @@ func generateUnionTypeCommon(gen *TypeGenerator, desc *SchemaDescriptor, isOneOf
1072975 if len (fixedFields ) > 0 {
1073976 gen .AddImport ("fmt" )
1074977 }
1075- gen .NeedHelper ("json_merge" )
1076978
1077979 code , err := GenerateUnionCode (cfg )
1078980 if err != nil {
0 commit comments