@@ -17,6 +17,7 @@ package nodejs
1717
1818import (
1919 "context"
20+ "encoding/json"
2021 "errors"
2122 "fmt"
2223 "os"
@@ -45,7 +46,7 @@ func Generate(ctx context.Context, library *config.Library, googleapisDir string
4546 return fmt .Errorf ("failed to generate api %q: %w" , api .Path , err )
4647 }
4748 }
48- if err := runPostProcessor (ctx , library , repoRoot , outdir ); err != nil {
49+ if err := runPostProcessor (ctx , library , googleapisDir , repoRoot , outdir ); err != nil {
4950 return fmt .Errorf ("failed to run post processor: %w" , err )
5051 }
5152 return nil
@@ -149,7 +150,7 @@ func buildGeneratorArgs(api *config.API, library *config.Library, googleapisDir,
149150
150151// runPostProcessor combines versioned API outputs from owl-bot-staging/ into
151152// the output directory using gapic-node-processing, then compiles protos.
152- func runPostProcessor (ctx context.Context , library * config.Library , repoRoot , outDir string ) error {
153+ func runPostProcessor (ctx context.Context , library * config.Library , googleapisDir , repoRoot , outDir string ) error {
153154 owlbotPath := filepath .Join (outDir , "owlbot.py" )
154155 if _ , err := os .Stat (owlbotPath ); err == nil {
155156 // Old way: use synthtool
@@ -205,6 +206,10 @@ func runPostProcessor(ctx context.Context, library *config.Library, repoRoot, ou
205206 }
206207 }
207208
209+ if err := copyMissingProtos (googleapisDir , outDir ); err != nil {
210+ return fmt .Errorf ("copyMissingProtos: %w" , err )
211+ }
212+
208213 if err := command .RunInDir (ctx , outDir , "compileProtos" , "src" ); err != nil {
209214 return fmt .Errorf ("compileProtos: %w" , err )
210215 }
@@ -251,7 +256,64 @@ func runPostProcessor(ctx context.Context, library *config.Library, repoRoot, ou
251256 return nil
252257}
253258
254- // Format runs eslint --fix on the library directory.
259+ // copyMissingProtos reads *_proto_list.json files under outDir/src/ and copies
260+ // any referenced protos that are missing from outDir/protos/ using the source
261+ // files in googleapisDir. The generator copies the API's own protos but not
262+ // transitive dependencies (e.g. google/logging/type/log_severity.proto).
263+ func copyMissingProtos (googleapisDir , outDir string ) error {
264+ googleapisDir , err := filepath .Abs (googleapisDir )
265+ if err != nil {
266+ return fmt .Errorf ("failed to resolve googleapis directory: %w" , err )
267+ }
268+
269+ lists , err := filepath .Glob (filepath .Join (outDir , "src" , "*" , "*_proto_list.json" ))
270+ if err != nil {
271+ return fmt .Errorf ("failed to glob proto list files: %w" , err )
272+ }
273+
274+ for _ , listPath := range lists {
275+ data , err := os .ReadFile (listPath )
276+ if err != nil {
277+ return fmt .Errorf ("failed to read %s: %w" , listPath , err )
278+ }
279+ var entries []string
280+ if err := json .Unmarshal (data , & entries ); err != nil {
281+ return fmt .Errorf ("failed to parse %s: %w" , listPath , err )
282+ }
283+
284+ listDir := filepath .Dir (listPath )
285+ for _ , entry := range entries {
286+ absPath := filepath .Join (listDir , entry )
287+ absPath = filepath .Clean (absPath )
288+ if _ , err := os .Stat (absPath ); err == nil {
289+ continue
290+ }
291+
292+ // Extract the proto-relative path after "protos/".
293+ const protosPrefix = "protos/"
294+ idx := strings .Index (entry , protosPrefix )
295+ if idx < 0 {
296+ continue
297+ }
298+ relPath := entry [idx + len (protosPrefix ):]
299+
300+ srcPath := filepath .Join (googleapisDir , relPath )
301+ content , err := os .ReadFile (srcPath )
302+ if err != nil {
303+ return fmt .Errorf ("failed to read source proto %s: %w" , srcPath , err )
304+ }
305+ if err := os .MkdirAll (filepath .Dir (absPath ), 0755 ); err != nil {
306+ return fmt .Errorf ("failed to create directory for %s: %w" , absPath , err )
307+ }
308+ if err := os .WriteFile (absPath , content , 0644 ); err != nil {
309+ return fmt .Errorf ("failed to write proto %s: %w" , absPath , err )
310+ }
311+ }
312+ }
313+ return nil
314+ }
315+
316+ // Format runs gts (npm run fix) on the library directory.
255317func Format (ctx context.Context , library * config.Library ) error {
256318 if err := ctx .Err (); err != nil {
257319 return err
0 commit comments