Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func Exists(path string) bool {
}

func FindDown(dir, filename string) []string {
return FindDownWithExcludes(dir, filename, []string{})
}

func FindDownWithExcludes(dir, filename string, excludePatterns []string) []string {
var result []string

filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
Expand All @@ -45,9 +49,14 @@ func FindDown(dir, filename string) []string {
}
if info.IsDir() {
name := info.Name()
// Always exclude node_modules and dot directories
if name == "node_modules" || strings.HasPrefix(name, ".") {
return filepath.SkipDir
}
// Check custom exclude patterns
if shouldExclude(name, excludePatterns) {
return filepath.SkipDir
}
}
if !info.IsDir() && info.Name() == filename {
result = append(result, path)
Expand All @@ -57,3 +66,22 @@ func FindDown(dir, filename string) []string {

return result
}

// shouldExclude checks if a directory name matches any of the exclude patterns
func shouldExclude(name string, patterns []string) bool {
for _, pattern := range patterns {
// Support glob patterns
matched, err := filepath.Match(pattern, name)
if err != nil {
// If pattern is invalid, try exact match as fallback
if name == pattern {
return true
}
continue
}
if matched {
return true
}
}
return false
}
170 changes: 170 additions & 0 deletions internal/fs/fs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package fs_test

import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/sst/sst/v3/internal/fs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestFindDown_WithExcludePatterns(t *testing.T) {
tests := []struct {
name string
setupDirs []string
setupFiles []string
searchFile string
excludePatterns []string
expectedCount int
shouldContain []string
shouldNotContain []string
}{
{
name: "basic - no excludes",
setupDirs: []string{"src", "lib"},
setupFiles: []string{"src/package.json", "lib/package.json"},
searchFile: "package.json",
excludePatterns: []string{},
expectedCount: 2,
shouldContain: []string{"src/package.json", "lib/package.json"},
shouldNotContain: []string{},
},
{
name: "exclude node_modules",
setupDirs: []string{"src", "node_modules", "node_modules/lib"},
setupFiles: []string{"src/package.json", "node_modules/package.json", "node_modules/lib/package.json"},
searchFile: "package.json",
excludePatterns: []string{"node_modules"},
expectedCount: 1,
shouldContain: []string{"src/package.json"},
shouldNotContain: []string{"node_modules"},
},
{
name: "exclude external directory",
setupDirs: []string{"src", "external", "external/submodule"},
setupFiles: []string{"src/package.json", "external/package.json", "external/submodule/package.json"},
searchFile: "package.json",
excludePatterns: []string{"external"},
expectedCount: 1,
shouldContain: []string{"src/package.json"},
shouldNotContain: []string{"external"},
},
{
name: "exclude multiple patterns",
setupDirs: []string{"src", "external", "vendor", "lib"},
setupFiles: []string{"src/package.json", "external/package.json", "vendor/package.json", "lib/package.json"},
searchFile: "package.json",
excludePatterns: []string{"external", "vendor"},
expectedCount: 2,
shouldContain: []string{"src/package.json", "lib/package.json"},
shouldNotContain: []string{"external", "vendor"},
},
{
name: "exclude with glob pattern",
setupDirs: []string{"src", "test-fixtures", "test-data", "lib"},
setupFiles: []string{"src/package.json", "test-fixtures/package.json", "test-data/package.json", "lib/package.json"},
searchFile: "package.json",
excludePatterns: []string{"test-*"},
expectedCount: 2,
shouldContain: []string{"src/package.json", "lib/package.json"},
shouldNotContain: []string{"test-fixtures", "test-data"},
},
{
name: "exclude nested directories",
setupDirs: []string{"src", "src/external", "src/external/deep", "lib"},
setupFiles: []string{"src/package.json", "src/external/package.json", "src/external/deep/package.json", "lib/package.json"},
searchFile: "package.json",
excludePatterns: []string{"external"},
expectedCount: 2,
shouldContain: []string{"src/package.json", "lib/package.json"},
shouldNotContain: []string{"external"},
},
{
name: "existing node_modules still excluded by default",
setupDirs: []string{"src", "node_modules"},
setupFiles: []string{"src/package.json", "node_modules/package.json"},
searchFile: "package.json",
excludePatterns: []string{}, // No custom excludes
expectedCount: 1,
shouldContain: []string{"src/package.json"},
shouldNotContain: []string{"node_modules"},
},
{
name: "existing dotfiles still excluded by default",
setupDirs: []string{"src", ".git", ".github"},
setupFiles: []string{"src/package.json", ".git/package.json", ".github/package.json"},
searchFile: "package.json",
excludePatterns: []string{}, // No custom excludes
expectedCount: 1,
shouldContain: []string{"src/package.json"},
shouldNotContain: []string{".git", ".github"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := t.TempDir()

// Setup directory structure
for _, dir := range tt.setupDirs {
err := os.MkdirAll(filepath.Join(tmpDir, dir), 0755)
require.NoError(t, err, "Failed to create directory: %s", dir)
}

// Create files
for _, file := range tt.setupFiles {
fullPath := filepath.Join(tmpDir, file)
f, err := os.Create(fullPath)
require.NoError(t, err, "Failed to create file: %s", file)
f.Close()
}

// Execute - this will fail until we implement the feature
results := fs.FindDownWithExcludes(tmpDir, tt.searchFile, tt.excludePatterns)

// Assert count
assert.Len(t, results, tt.expectedCount, "Expected %d results, got %d", tt.expectedCount, len(results))

// Assert contains expected paths
for _, expectedPath := range tt.shouldContain {
assert.True(t, containsPath(results, expectedPath),
"Results should contain path with: %s\nGot: %v", expectedPath, results)
}

// Assert does not contain excluded paths
for _, excludedPath := range tt.shouldNotContain {
assert.False(t, containsPath(results, excludedPath),
"Results should NOT contain path with: %s\nGot: %v", excludedPath, results)
}
})
}
}

// Helper to check if results contain a path ending with the given suffix
func containsPath(results []string, pathSuffix string) bool {
for _, r := range results {
// Normalize both paths and check if result ends with the suffix
cleanResult := filepath.Clean(r)
cleanSuffix := filepath.Clean(pathSuffix)

// Check if it's a suffix match (e.g., "src/package.json" matches ".../src/package.json")
if strings.HasSuffix(cleanResult, string(filepath.Separator)+cleanSuffix) ||
strings.HasSuffix(cleanResult, cleanSuffix) {
return true
}

// Also check if pathSuffix is just a directory name component (for shouldNotContain checks)
if !strings.Contains(pathSuffix, string(filepath.Separator)) {
parts := strings.Split(cleanResult, string(filepath.Separator))
for _, part := range parts {
if part == pathSuffix {
return true
}
}
}
}
return false
}
1 change: 1 addition & 0 deletions pkg/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type App struct {
Home string `json:"home"`
Version string `json:"version"`
Protect bool `json:"protect"`
Exclude []string `json:"exclude"`
// Deprecated: Backend is now Home
Backend string `json:"backend"`
// Deprecated: RemovalPolicy is now Removal
Expand Down
2 changes: 1 addition & 1 deletion pkg/project/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ loop:
complete.Finished = finished
complete.Errors = errors
complete.ImportDiffs = importDiffs
types.Generate(p.PathConfig(), complete.Links)
types.Generate(p.PathConfig(), complete.Links, p.app.Exclude)
defer bus.Publish(complete)

if input.Command != "diff" {
Expand Down
4 changes: 2 additions & 2 deletions pkg/types/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/sst/sst/v3/pkg/project/common"
)

func Generate(root string, links common.Links) error {
projects := fs.FindDown(root, "pyproject.toml")
func Generate(root string, links common.Links, exclude []string) error {
projects := fs.FindDownWithExcludes(root, "pyproject.toml", exclude)
files := []io.Writer{}
for _, project := range projects {
path := filepath.Join(filepath.Dir(project), "sst.pyi")
Expand Down
4 changes: 2 additions & 2 deletions pkg/types/rails/rails.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"github.com/sst/sst/v3/pkg/project/common"
)

func Generate(root string, links common.Links) error {
func Generate(root string, links common.Links, exclude []string) error {
return nil
projects := fs.FindDown(root, "config.ru")
projects := fs.FindDownWithExcludes(root, "config.ru", exclude)
files := []io.Writer{}
for _, project := range projects {
// check if lib path exists
Expand Down
8 changes: 4 additions & 4 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import (
"github.com/sst/sst/v3/pkg/types/typescript"
)

type Generator = func(root string, complete common.Links) error
type Generator = func(root string, complete common.Links, exclude []string) error

func Generate(cfgPath string, complete common.Links) error {
func Generate(cfgPath string, complete common.Links, exclude []string) error {
root := path.ResolveRootDir(cfgPath)
// gitroot, err := fs.FindUp(root, ".git")
// if err == nil {
// root = filepath.Dir(gitroot)
// }
slog.Info("generating types", "root", root)
slog.Info("generating types", "root", root, "exclude", exclude)
for _, generator := range All {
err := generator(root, complete)
err := generator(root, complete, exclude)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/types/typescript/typescript.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var mapping = map[string]string{
"serviceBindings": "Service",
}

func Generate(root string, links common.Links) error {
func Generate(root string, links common.Links, exclude []string) error {
cloudflareBindings := map[string]string{}
for name, link := range links {
for _, include := range link.Include {
Expand All @@ -46,7 +46,7 @@ func Generate(root string, links common.Links) error {
"export {}",
}, "\n"))

packageJsons := fs.FindDown(root, "package.json")
packageJsons := fs.FindDownWithExcludes(root, "package.json", exclude)
rootEnv := filepath.Join(root, "sst-env.d.ts")
for _, packageJson := range packageJsons {
packageJsonFile, err := os.Open(packageJson)
Expand Down