88 "log/slog"
99 "os"
1010 "os/exec"
11- "path"
1211 "path/filepath"
12+ "regexp"
1313 "strings"
1414
1515 "github.com/bufbuild/protocompile"
@@ -110,7 +110,18 @@ func (c *Core) Generate(ctx context.Context, root, directory string) error {
110110 }
111111
112112 for _ , inputFilesDir := range c .inputs .InputFilesDir {
113- fsWalker := fs .NewFSWalker (directory , inputFilesDir .Root )
113+ searchPath := filepath .Join (inputFilesDir .Root , inputFilesDir .Path )
114+ // Skip if inputFilesDir.Root and directory don't overlap
115+ if directory != "." && ! pathsOverlap (directory , searchPath ) {
116+ c .logger .DebugContext (ctx , "skipping inputFilesDir" ,
117+ slog .String ("directory" , directory ),
118+ slog .String ("searchPath" , searchPath ),
119+ slog .String ("reason" , "paths don't overlap" ),
120+ )
121+ continue
122+ }
123+
124+ fsWalker := fs .NewFSWalker (root , searchPath )
114125 q .Imports = append (q .Imports , inputFilesDir .Root )
115126
116127 err := fsWalker .WalkDir (func (walkPath string , err error ) error {
@@ -121,12 +132,13 @@ func (c *Core) Generate(ctx context.Context, root, directory string) error {
121132 return ctx .Err ()
122133 case filepath .Ext (walkPath ) != ".proto" :
123134 return nil
124- case shouldIgnore (walkPath , []string {path .Join (inputFilesDir .Root , inputFilesDir .Path )}):
125- c .logger .DebugContext (ctx , "ignore" , slog .String ("walkPath" , walkPath ))
126-
135+ case shouldIgnore (walkPath , []string {directory }):
136+ c .logger .DebugContext (ctx , "ignore" , slog .String ("walkPath" , walkPath ), slog .String ("directory" , directory ))
127137 return nil
128138 }
129139
140+ // Get file path relative to inputFilesDir.Root
141+ // walkPath starts with inputFilesDir.Root
130142 addedFile := stripPrefix (walkPath , inputFilesDir .Root )
131143 q .Files = append (q .Files , addedFile )
132144
@@ -286,9 +298,9 @@ func (c *Core) Generate(ctx context.Context, root, directory string) error {
286298 // Determine base directory for output files considering plugin.Out
287299 var baseDir string
288300 if plugin .Out != "" {
289- baseDir = filepath .Join (directory , plugin .Out )
301+ baseDir = filepath .Join (root , plugin .Out )
290302 } else {
291- baseDir = directory
303+ baseDir = root
292304 }
293305
294306 p := filepath .Join (baseDir , file .GetName ())
@@ -354,14 +366,69 @@ func addFileWithInsertionPoint(
354366 return nil
355367}
356368
369+ // pathsOverlap checks if two paths overlap (one is within another or they are equal).
370+ // It is recommended to pass absolute paths.
371+ func pathsOverlap (a , b string ) bool {
372+ na := filepath .Clean (a )
373+ nb := filepath .Clean (b )
374+
375+ // Full match is always overlap
376+ if na == nb {
377+ return true
378+ }
379+
380+ // Add separator at the end to distinguish "/foo/bar" from "/foo/bark"
381+ naSlash := na + string (filepath .Separator )
382+ nbSlash := nb + string (filepath .Separator )
383+
384+ // na is parent of nb
385+ if strings .HasPrefix (nbSlash , naSlash ) {
386+ return true
387+ }
388+
389+ // nb is parent of na
390+ if strings .HasPrefix (naSlash , nbSlash ) {
391+ return true
392+ }
393+
394+ return false
395+ }
396+
357397func shouldIgnore (path string , dirs []string ) bool {
398+ path = filepath .Clean (path )
358399 if len (dirs ) == 0 {
359400 return true
360401 }
361402
362403 for _ , dir := range dirs {
363- if strings .HasPrefix (path , dir ) {
364- return false
404+ dir = filepath .Clean (dir )
405+
406+ // Special case: if dir is ".", match everything
407+ if dir == "." {
408+ slog .Debug ("shouldIgnore: dir is '.', matching all paths" , "path" , path )
409+ return false // Don't ignore - match everything
410+ }
411+
412+ // Check if path starts with dir (prefix matching)
413+ if strings .HasPrefix (path , dir + "/" ) || path == dir {
414+ slog .Debug ("shouldIgnore: path starts with dir" , "path" , path , "dir" , dir )
415+ return false // Don't ignore - path is within directory
416+ }
417+
418+ // Check regex pattern (for wildcard patterns)
419+ // QuoteMeta escapes all special chars (including *), then we convert \* back to .* for wildcard matching
420+ pattern := regexp .QuoteMeta (dir )
421+ pattern = strings .ReplaceAll (pattern , "\\ *" , ".*" )
422+ regexPattern := "^" + pattern
423+
424+ matched , err := regexp .MatchString (regexPattern , path )
425+ if err != nil {
426+ slog .Warn ("shouldIgnore: regex match error" , "path" , path , "dir" , dir , "regex" , regexPattern , "error" , err )
427+ continue
428+ }
429+ if matched {
430+ slog .Debug ("shouldIgnore: path matches regex pattern" , "path" , path , "dir" , dir , "regex" , regexPattern )
431+ return false // Don't ignore - path matches pattern
365432 }
366433 }
367434
@@ -395,6 +462,8 @@ func (c *Core) getModulePath(ctx context.Context, requestedDependency string) (s
395462func stripPrefix (path , prefix string ) string {
396463 normalizedPath := filepath .ToSlash (path )
397464 normalizedPrefix := filepath .ToSlash (prefix )
465+ // Remove trailing slash from prefix if present
466+ normalizedPrefix = strings .TrimSuffix (normalizedPrefix , "/" )
398467
399468 return strings .TrimPrefix (normalizedPath , normalizedPrefix + "/" )
400469}
0 commit comments