diff --git a/WORKSPACE b/WORKSPACE index 744d02b87..1ec81b2aa 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -164,6 +164,7 @@ http_archive( patches = [ "//:patches/bazelbuild_bazel-gazelle_aspect-cli.patch", "//:patches/bazelbuild_bazel-gazelle_aspect-walk-subdir.patch", + "//:patches/bazelbuild_bazel-gazelle_aspect-gitignore.patch", ], sha256 = "872f1532567cdc53dc8e9f4681cd45021cd6787e2bde8a022bcec24a5867ce4c", # Ensure this version always matches the go.mod version. diff --git a/gazelle/common/git/BUILD.bazel b/gazelle/common/git/BUILD.bazel index 22353371f..cee94d70b 100644 --- a/gazelle/common/git/BUILD.bazel +++ b/gazelle/common/git/BUILD.bazel @@ -16,4 +16,5 @@ go_test( name = "git_test", srcs = ["gitignore_test.go"], embed = [":git"], + deps = ["@bazel_gazelle//config:go_default_library"], ) diff --git a/gazelle/common/git/gitignore.go b/gazelle/common/git/gitignore.go index 050eeaced..546acb00e 100644 --- a/gazelle/common/git/gitignore.go +++ b/gazelle/common/git/gitignore.go @@ -12,44 +12,74 @@ import ( gitignore "github.com/go-git/go-git/v5/plumbing/format/gitignore" ) -// Wrap the ignore files along with the relative path they were loaded from -// to enable quick-exit checks. -type ignoreEntry struct { - i gitignore.Matcher - base string -} +// Must align with patched bazel-gazelle +const ASPECT_GITIGNORE = "__aspect:gitignore" -type GitIgnore struct { - ignores []ignoreEntry -} +// Directive to enable/disable gitignore support +const Directive_GitIgnore = "gitignore" + +// Internal +const enabledExt = Directive_GitIgnore +const lastConfiguredExt = "gitignore:dir" +const ignorePatternsExt = "gitignore:patterns" -func NewGitIgnore() *GitIgnore { - return &GitIgnore{ - ignores: make([]ignoreEntry, 0), +func CollectIgnoreFiles(c *config.Config, rel string) { + // Only parse once per directory. + if lastCollected, hasCollected := c.Exts[lastConfiguredExt]; hasCollected && lastCollected == rel { + return } -} + c.Exts[lastConfiguredExt] = rel -func (i *GitIgnore) CollectIgnoreFiles(c *config.Config, rel string) { - // Collect gitignore style ignore files in this directory. + // Find and add .gitignore files from this directory ignoreFilePath := path.Join(c.RepoRoot, rel, ".gitignore") - - if ignoreReader, ignoreErr := os.Open(ignoreFilePath); ignoreErr == nil { + ignoreReader, ignoreErr := os.Open(ignoreFilePath) + if ignoreErr == nil { BazelLog.Tracef("Add ignore file %s/.gitignore", rel) + defer ignoreReader.Close() + addIgnore(c, rel, ignoreReader) + } else if !os.IsNotExist(ignoreErr) { + BazelLog.Errorf("Failed to open %s/.gitignore: %v", rel, ignoreErr) + } +} - i.addIgnore(rel, ignoreReader) +func EnableGitignore(c *config.Config, enabled bool) { + c.Exts[enabledExt] = enabled + if enabled { + c.Exts[ASPECT_GITIGNORE] = createMatcherFunc(c) + } else { + c.Exts[ASPECT_GITIGNORE] = nil } } -func (i *GitIgnore) addIgnore(rel string, ignoreReader io.Reader) { - // Persist a relative path to the ignore file to enable quick-exit checks. - base := path.Clean(rel) - if base == "." { - base = "" +func isEnabled(c *config.Config) bool { + enabled, hasEnabled := c.Exts[enabledExt] + return hasEnabled && enabled.(bool) +} + +func addIgnore(c *config.Config, rel string, ignoreReader io.Reader) { + var ignorePatterns []gitignore.Pattern + + // Load parent ignore patterns + if c.Exts[ignorePatternsExt] != nil { + ignorePatterns = c.Exts[ignorePatternsExt].([]gitignore.Pattern) + } + + // Append new ignore patterns + ignorePatterns = append(ignorePatterns, parseIgnore(rel, ignoreReader)...) + + // Persist appended ignore patterns + c.Exts[ignorePatternsExt] = ignorePatterns + + // Persist a matcher function with the updated ignore patterns if enabled + if isEnabled(c) { + c.Exts[ASPECT_GITIGNORE] = createMatcherFunc(c) } +} - domain := []string{} - if base != "" { - domain = strings.Split(base, "/") +func parseIgnore(rel string, ignoreReader io.Reader) []gitignore.Pattern { + var domain []string + if rel != "" { + domain = strings.Split(path.Clean(rel), "/") } matcherPatterns := make([]gitignore.Pattern, 0) @@ -64,31 +94,17 @@ func (i *GitIgnore) addIgnore(rel string, ignoreReader io.Reader) { matcherPatterns = append(matcherPatterns, gitignore.ParsePattern(p, domain)) } - ignore := gitignore.NewMatcher(matcherPatterns) - - // Add a trailing slash to the base path to ensure the ignore file only - // processes paths within that directory. - if base != "" && !strings.HasSuffix(base, "/") { - base += "/" - } - - i.ignores = append(i.ignores, ignoreEntry{ - i: ignore, - base: base, - }) + return matcherPatterns } -func (i *GitIgnore) Matches(p string) bool { - for _, ignore := range i.ignores { - // Ensure the path is within the base path of the ignore file - // to avoid strings.Split unless necessary. - if !strings.HasPrefix(p, ignore.base) { - continue - } - if ignore.i.Match(strings.Split(p, "/"), false) { - return true - } +func createMatcherFunc(c *config.Config) func(string) bool { + patterns, patternsFound := c.Exts[ignorePatternsExt] + if !patternsFound { + return nil } - return false + matcher := gitignore.NewMatcher(patterns.([]gitignore.Pattern)) + return func(s string) bool { + return matcher.Match(strings.Split(s, "/"), false) + } } diff --git a/gazelle/common/git/gitignore_test.go b/gazelle/common/git/gitignore_test.go index 0e38aa242..edbdc3d53 100644 --- a/gazelle/common/git/gitignore_test.go +++ b/gazelle/common/git/gitignore_test.go @@ -4,89 +4,93 @@ import ( "fmt" "strings" "testing" + + "github.com/bazelbuild/bazel-gazelle/config" ) func TestGitIgnore(t *testing.T) { - shouldMatch := func(what string, i *GitIgnore, matches ...string) { + shouldMatch := func(what string, c *config.Config, matches ...string) { + matcher := createMatcherFunc(c) for _, m := range matches { - if !i.Matches(m) { + if !(matcher != nil && matcher(m)) { t.Error(fmt.Sprintf("%s should match '%s'", what, m)) } } } - shouldNotMatch := func(what string, i *GitIgnore, matches ...string) { + shouldNotMatch := func(what string, c *config.Config, matches ...string) { + matcher := createMatcherFunc(c) for _, m := range matches { - if i.Matches(m) { + if matcher != nil && matcher(m) { t.Error(fmt.Sprintf("%s should NOT match '%s'", what, m)) } } } t.Run("basic", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` # comment, that is indented a.js b/c.js `) - shouldMatch("exact matches", i, "a.js", "b/c.js") + shouldMatch("exact matches", c, "a.js", "b/c.js") }) t.Run("partial paths", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` a.js b/c.js # b.js `) - shouldNotMatch("partial matches", i, "a", "b", "b/c", "c.js") - shouldNotMatch("files in comments", i, "b.js") + shouldNotMatch("partial matches", c, "a", "b", "b/c", "c.js") + shouldNotMatch("files in comments", c, "b.js") }) t.Run("nested ignore matches", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` a.js `) - addIgnoreFileContent(i, "b", ` + addIgnoreFileContent(c, "b", ` c.js `) - shouldMatch("subdirectory patterns", i, "a.js", "b/c.js") + shouldMatch("subdirectory patterns", c, "a.js", "b/c.js") }) t.Run("overlapping ignore matches", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` a.js b/c/d/e.js `) - addIgnoreFileContent(i, "b", ` + addIgnoreFileContent(c, "b", ` asdf.js `) - shouldMatch("overlapping paths", i, "a.js", "b/c/d/e.js", "b/asdf.js") - shouldNotMatch("subdir on parent dir pattrn", i, "asdf.js") + shouldMatch("overlapping paths", c, "a.js", "b/c/d/e.js", "b/asdf.js") + shouldNotMatch("subdir on parent dir pattrn", c, "asdf.js") }) t.Run("star dot", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` *.js a/*.js `) - shouldMatch("star dot", i, "a.js", "A.js", "_.js", "b.js") - shouldMatch("subdir star dot", i, "a/b.js", "a/abcd.js", "a/_.js", "a/.js", ".js", "x/y/z/a/b.js") - shouldNotMatch("partial star dot", i, "a", "a/", "a.jsx", "a/b") + shouldMatch("star dot", c, "a.js", "A.js", "_.js", "b.js") + shouldMatch("subdir star dot", c, "a/b.js", "a/abcd.js", "a/_.js", "a/.js", ".js", "x/y/z/a/b.js") + shouldNotMatch("partial star dot", c, "a", "a/", "a.jsx", "a/b") }) t.Run("stars", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, ".", ` + c := config.New() + addIgnoreFileContent(c, "", ` # A file by name only in root r1.ts @@ -102,88 +106,88 @@ func TestGitIgnore(t *testing.T) { all-within/** `) - shouldMatch("exact", i, "r1.ts", "x/r1.ts", "subdir/direct-ig.ts") - shouldNotMatch("different dirs", i, "othersub/direct-ig.ts", "x/subdir/direct-ig.ts", "subdir/x/direct-ig.ts") + shouldMatch("exact", c, "r1.ts", "x/r1.ts", "subdir/direct-ig.ts") + shouldNotMatch("different dirs", c, "othersub/direct-ig.ts", "x/subdir/direct-ig.ts", "subdir/x/direct-ig.ts") - shouldMatch("star", i, "x/star-ig.ts") - shouldNotMatch("start missing dir", i, "star-ig.ts", "subdir/x/star-ig.ts", "a/b/c/x/star-ig.ts") + shouldMatch("star", c, "x/star-ig.ts") + shouldNotMatch("start missing dir", c, "star-ig.ts", "subdir/x/star-ig.ts", "a/b/c/x/star-ig.ts") - shouldMatch("double wildcard", i, "x.starstar-ig.ts", "subdir/x.starstar-ig.ts", "a/b/c/x.starstar-ig.ts", "a/.starstar-ig.ts") - shouldNotMatch("double wildcard", i, ".startstar-ig.ts", "subdir/.startstar-ig.ts", "a/starstar-ig.ts") + shouldMatch("double wildcard", c, "x.starstar-ig.ts", "subdir/x.starstar-ig.ts", "a/b/c/x.starstar-ig.ts", "a/.starstar-ig.ts") + shouldNotMatch("double wildcard", c, ".startstar-ig.ts", "subdir/.startstar-ig.ts", "a/starstar-ig.ts") - shouldMatch("all within", i, "all-within/x.ts", "all-within/subdir/x.ts", "all-within/a/b/c/x.ts") - shouldNotMatch("all within", i, "x/all-within/x.tsx", "y/all-within/subdir/x.tsx") + shouldMatch("all within", c, "all-within/x.ts", "all-within/subdir/x.ts", "all-within/a/b/c/x.ts") + shouldNotMatch("all within", c, "x/all-within/x.tsx", "y/all-within/subdir/x.tsx") }) t.Run("subdir matches", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, "subdir", ` + c := config.New() + addIgnoreFileContent(c, "subdir", ` node_modules dir_slash/ dir_slash_star/* dir_slash_doublestar/** `) - shouldMatch("no slash", i, "subdir/node_modules", "subdir/node_modules/x.ts", "subdir/node_modules/m/x.ts") + shouldMatch("no slash", c, "subdir/node_modules", "subdir/node_modules/x.ts", "subdir/node_modules/m/x.ts") - shouldMatch("slash", i, "subdir/dir_slash/", "subdir/dir_slash/x.ts", "subdir/dir_slash/m/x.ts") - shouldMatch("slash star", i, "subdir/dir_slash_star/x.ts", "subdir/dir_slash_star/m/x.ts") + shouldMatch("slash", c, "subdir/dir_slash/", "subdir/dir_slash/x.ts", "subdir/dir_slash/m/x.ts") + shouldMatch("slash star", c, "subdir/dir_slash_star/x.ts", "subdir/dir_slash_star/m/x.ts") - shouldNotMatch("slash star must have star content", i, "subdir/dir_slash_star") + shouldNotMatch("slash star must have star content", c, "subdir/dir_slash_star") - shouldMatch("slash double star", i, "subdir/dir_slash_doublestar/x.ts", "subdir/dir_slash_doublestar/m/x.ts") - shouldMatch("slash double star does not require content", i, "subdir/dir_slash_doublestar") + shouldMatch("slash double star", c, "subdir/dir_slash_doublestar/x.ts", "subdir/dir_slash_doublestar/m/x.ts") + shouldMatch("slash double star does not require content", c, "subdir/dir_slash_doublestar") }) t.Run("subdir matches all", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, "subdir", "*") + c := config.New() + addIgnoreFileContent(c, "subdir", "*") - shouldMatch("all", i, "subdir/x", "subdir/x/y", "subdir/a.b", "subdir/a/b.c") - shouldNotMatch("other dirs", i, "x", "x.y", "b/subdir", "b/subdir/x") + shouldMatch("all", c, "subdir/x", "subdir/x/y", "subdir/a.b", "subdir/a/b.c") + shouldNotMatch("other dirs", c, "x", "x.y", "b/subdir", "b/subdir/x") }) t.Run("subdir matches exact name", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, "subdir", ` + c := config.New() + addIgnoreFileContent(c, "subdir", ` r1.ts sub2/r2.ts `) - shouldMatch("exact name abs", i, "subdir/r1.ts", "subdir/deeper/sub/dir/r1.ts", "subdir/sub2/r2.ts") - shouldNotMatch("different dirs", i, "r1.ts", "othersub/r1.ts", "r2.ts", "othersub/r2.ts", "subdir/r2.ts", "subdir/other/r2.ts") + shouldMatch("exact name abs", c, "subdir/r1.ts", "subdir/deeper/sub/dir/r1.ts", "subdir/sub2/r2.ts") + shouldNotMatch("different dirs", c, "r1.ts", "othersub/r1.ts", "r2.ts", "othersub/r2.ts", "subdir/r2.ts", "subdir/other/r2.ts") }) t.Run("stars subdir", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, "subdir", ` + c := config.New() + addIgnoreFileContent(c, "subdir", ` */star.ts **/starstarslash.ts `) - shouldMatch("star", i, "subdir/x/star.ts") - shouldNotMatch("start different dirs", i, "star.ts", "a/b/c/x/star.ts") - shouldNotMatch("start different subdirs", i, "subdir/x/y/star.ts") + shouldMatch("star", c, "subdir/x/star.ts") + shouldNotMatch("start different dirs", c, "star.ts", "a/b/c/x/star.ts") + shouldNotMatch("start different subdirs", c, "subdir/x/y/star.ts") - shouldMatch("double wildcard slash", i, "subdir/starstarslash.ts", "subdir/a/starstarslash.ts", "subdir/a/b/c/starstarslash.ts") - shouldNotMatch("double wildcard slash different name pre", i, "subdir/x.starstarslash.ts", "subdir/.starstarslash.ts") - shouldNotMatch("double wildcard slash different subdir", i, "a/x.starstarslash.ts", "a/b/c/starstarslash.ts") + shouldMatch("double wildcard slash", c, "subdir/starstarslash.ts", "subdir/a/starstarslash.ts", "subdir/a/b/c/starstarslash.ts") + shouldNotMatch("double wildcard slash different name pre", c, "subdir/x.starstarslash.ts", "subdir/.starstarslash.ts") + shouldNotMatch("double wildcard slash different subdir", c, "a/x.starstarslash.ts", "a/b/c/starstarslash.ts") }) t.Run("doublestar no slash", func(t *testing.T) { - i := NewGitIgnore() - addIgnoreFileContent(i, "subdir", ` + c := config.New() + addIgnoreFileContent(c, "subdir", ` **starstar.ts `) - shouldMatch("double wildcard", i, "subdir/x.starstar.ts", "subdir/a/b/c/x.starstar.ts", "subdir/a/.starstar.ts") - shouldNotMatch("double wildcard different subdir", i, "startstar.ts", ".startstar.ts", "a/starstar.ts") + shouldMatch("double wildcard", c, "subdir/x.starstar.ts", "subdir/a/b/c/x.starstar.ts", "subdir/a/.starstar.ts") + shouldNotMatch("double wildcard different subdir", c, "startstar.ts", ".startstar.ts", "a/starstar.ts") }) } // Util method to invoke GitIgnore.AddIgnore() with the trimmed string // value to allow tests to be written with multiline strings including indentation. -func addIgnoreFileContent(i *GitIgnore, rel, ignoreContents string) { +func addIgnoreFileContent(c *config.Config, rel, ignoreContents string) { ignoreLines := make([]string, 0) for _, line := range strings.Split(ignoreContents, "\n") { if trimmdLine := strings.TrimSpace(line); trimmdLine != "" { @@ -191,5 +195,5 @@ func addIgnoreFileContent(i *GitIgnore, rel, ignoreContents string) { } } - i.addIgnore(rel, strings.NewReader(strings.Join(ignoreLines, "\n"))) + addIgnore(c, rel, strings.NewReader(strings.Join(ignoreLines, "\n"))) } diff --git a/gazelle/common/walk.go b/gazelle/common/walk.go index 076812f3a..c1555dcaf 100644 --- a/gazelle/common/walk.go +++ b/gazelle/common/walk.go @@ -1,7 +1,6 @@ package gazelle import ( - "path" "path/filepath" BazelLog "aspect.build/cli/pkg/logger" @@ -9,14 +8,13 @@ import ( ) type GazelleWalkFunc func(path string) error -type GazelleWalkIgnoreFunc func(path string) bool // Must align with patched bazel-gazelle const ASPECT_WALKSUBDIR = "__aspect:walksubdir" // Walk the directory of the language.GenerateArgs, optionally recursing into // subdirectories unlike the files provided in GenerateArgs.RegularFiles. -func GazelleWalkDir(args language.GenerateArgs, isIgnored GazelleWalkIgnoreFunc, walkFunc GazelleWalkFunc) error { +func GazelleWalkDir(args language.GenerateArgs, walkFunc GazelleWalkFunc) error { BazelLog.Tracef("GazelleWalkDir: %s", args.Rel) // Source files in the primary directory @@ -26,11 +24,6 @@ func GazelleWalkDir(args language.GenerateArgs, isIgnored GazelleWalkIgnoreFunc, continue } - if isIgnored(path.Join(args.Rel, f)) { - BazelLog.Tracef("File ignored: %s / %s", args.Rel, f) - continue - } - BazelLog.Tracef("GazelleWalkDir RegularFile: %s", f) walkErr := walkFunc(f) diff --git a/gazelle/js/config.go b/gazelle/js/config.go index f05e34b1e..73384cabd 100644 --- a/gazelle/js/config.go +++ b/gazelle/js/config.go @@ -64,9 +64,6 @@ const ( Directive_CustomTargetTestFiles = "js_custom_test_files" // TODO(deprecated): remove - replaced with common generation_mode Directive_GenerationMode = "js_generation_mode" - - // TODO: move to common - Directive_GitIgnore = "gitignore" ) type NpmPackageMode string @@ -176,9 +173,6 @@ type JsGazelleConfig struct { packageTargetKind PackageTargetKind - // TODO: move to common util - gitignoreEnabled bool - protoGenerationEnabled bool tsconfigGenerationEnabled bool packageGenerationEnabled NpmPackageMode @@ -220,10 +214,6 @@ func newRootConfig() *JsGazelleConfig { tsProtoLibraryName: DefaultProtoLibraryName, targets: DefaultSourceGlobs[:], defaultTsconfigName: defaultTsconfigName, - - // TODO: move to common - // TODO: switch default to false - gitignoreEnabled: true, } } @@ -291,11 +281,6 @@ func (c *JsGazelleConfig) SetVisibility(groupName string, visLabels []string) { } } -// TODO: move to common util -func (c *JsGazelleConfig) SetGitIgnoreEnabled(enabled bool) { - c.gitignoreEnabled = enabled -} - // SetGenerationEnabled sets whether the extension is enabled or not. func (c *JsGazelleConfig) SetGenerationEnabled(enabled bool) { c.generationEnabled = enabled diff --git a/gazelle/js/configure.go b/gazelle/js/configure.go index 9d88eac75..ec29e3a77 100644 --- a/gazelle/js/configure.go +++ b/gazelle/js/configure.go @@ -9,6 +9,7 @@ import ( "strings" common "aspect.build/cli/gazelle/common" + "aspect.build/cli/gazelle/common/git" BazelLog "aspect.build/cli/pkg/logger" "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/label" @@ -23,7 +24,11 @@ var _ config.Configurer = (*typeScriptLang)(nil) // method is called once with the root configuration when Gazelle // starts. RegisterFlags may set an initial values in Config.Exts. When flags // are set, they should modify these values. -func (ts *typeScriptLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {} +func (ts *typeScriptLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) { + // Enable .gitignore support by default + // TODO: change to false and encourage .bazelignore + git.EnableGitignore(c, true) +} // CheckFlags validates the configuration after command line flags are parsed. // This is called once with the root configuration when Gazelle starts. @@ -62,7 +67,7 @@ func (ts *typeScriptLang) KnownDirectives() []string { Directive_CustomTargetTestFiles, // TODO: move to common - Directive_GitIgnore, + git.Directive_GitIgnore, } } @@ -98,7 +103,7 @@ func (ts *typeScriptLang) Configure(c *config.Config, rel string, f *rule.File) // Enable the WALKSUBDIR gazelle patch, setting the flag depending on js the GenerationMode. c.Exts[common.ASPECT_WALKSUBDIR] = c.Exts[LanguageName].(*JsGazelleConfig).generationMode == common.GenerationModeUpdate - ts.gitignore.CollectIgnoreFiles(c, rel) + git.CollectIgnoreFiles(c, rel) } func (ts *typeScriptLang) readConfigurations(c *config.Config, rel string) { @@ -125,8 +130,6 @@ func (ts *typeScriptLang) readDirectives(c *config.Config, rel string, f *rule.F value := strings.TrimSpace(d.Value) switch d.Key { - case Directive_GitIgnore: - config.SetGitIgnoreEnabled(common.ReadEnabled(d)) case Directive_TypeScriptExtension: config.SetGenerationEnabled(common.ReadEnabled(d)) case Directive_TypeScriptConfigExtension: @@ -275,6 +278,9 @@ func (ts *typeScriptLang) readDirectives(c *config.Config, rel string, f *rule.F default: log.Fatalf("invalid value for directive %q: %s", common.Directive_GenerationMode, d.Value) } + // TODO: move to common + case git.Directive_GitIgnore: + git.EnableGitignore(c, common.ReadEnabled(d)) } } } diff --git a/gazelle/js/generate.go b/gazelle/js/generate.go index 1263835da..ee6e7f5b8 100644 --- a/gazelle/js/generate.go +++ b/gazelle/js/generate.go @@ -677,18 +677,11 @@ func parseSourceFile(rootDir, filePath string) (parser.ParseResult, []error) { return parser.ParseSource(filePath, string(content)) } -var ignoreNone = func(string) bool { return false } - func (ts *typeScriptLang) collectSourceFiles(cfg *JsGazelleConfig, args language.GenerateArgs) (*treeset.Set, *treeset.Set, error) { sourceFiles := treeset.NewWithStringComparator() dataFiles := treeset.NewWithStringComparator() - ignoreMatch := ignoreNone - if cfg.gitignoreEnabled { - ignoreMatch = ts.gitignore.Matches - } - - err := gazelle.GazelleWalkDir(args, ignoreMatch, func(f string) error { + err := gazelle.GazelleWalkDir(args, func(f string) error { // Excluded due to being outside the ts root if !ts.tsconfig.IsWithinTsRoot(f) { BazelLog.Debugf("Skip %q outside rootDir\n", f) diff --git a/gazelle/js/language.go b/gazelle/js/language.go index ed8413999..27216f7bd 100644 --- a/gazelle/js/language.go +++ b/gazelle/js/language.go @@ -1,7 +1,6 @@ package gazelle import ( - git "aspect.build/cli/gazelle/common/git" pnpm "aspect.build/cli/gazelle/js/pnpm" "aspect.build/cli/gazelle/js/typescript" "github.com/bazelbuild/bazel-gazelle/label" @@ -31,9 +30,6 @@ type typeScriptLang struct { // TypeScript configuration across the workspace tsconfig *typescript.TsWorkspace - - // Ignore configurations for the workspace. - gitignore *git.GitIgnore } // NewLanguage initializes a new TypeScript that satisfies the language.Language @@ -46,6 +42,5 @@ func NewLanguage() language.Language { moduleTypes: make(map[string][]*label.Label), pnpmProjects: pnpmProjects, tsconfig: typescript.NewTsWorkspace(pnpmProjects), - gitignore: git.NewGitIgnore(), } } diff --git a/gazelle/js/tests/ignore_config_files/root.starstar-ig.ts b/gazelle/js/tests/ignore_config_files/root.starstar-ig.ts new file mode 100644 index 000000000..fc5a8dec8 --- /dev/null +++ b/gazelle/js/tests/ignore_config_files/root.starstar-ig.ts @@ -0,0 +1 @@ +import 'bad-import-but-this-file-is-ignored'; diff --git a/gazelle/js/tests/ignore_config_files/subdir/nested/y.starstar-ig.ts b/gazelle/js/tests/ignore_config_files/subdir/nested/y.starstar-ig.ts index e69de29bb..fc5a8dec8 100644 --- a/gazelle/js/tests/ignore_config_files/subdir/nested/y.starstar-ig.ts +++ b/gazelle/js/tests/ignore_config_files/subdir/nested/y.starstar-ig.ts @@ -0,0 +1 @@ +import 'bad-import-but-this-file-is-ignored'; diff --git a/gazelle/kotlin/configure.go b/gazelle/kotlin/configure.go index 3d8078afc..0dffbc700 100644 --- a/gazelle/kotlin/configure.go +++ b/gazelle/kotlin/configure.go @@ -4,6 +4,7 @@ import ( "flag" common "aspect.build/cli/gazelle/common" + "aspect.build/cli/gazelle/common/git" "aspect.build/cli/gazelle/kotlin/kotlinconfig" BazelLog "aspect.build/cli/pkg/logger" jvm_javaconfig "github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig" @@ -19,6 +20,9 @@ func (kt *kotlinLang) KnownDirectives() []string { return []string{ kotlinconfig.Directive_KotlinExtension, jvm_javaconfig.JavaMavenInstallFile, + + // TODO: move to common + git.Directive_GitIgnore, } } @@ -44,7 +48,7 @@ func (kt *kotlinLang) Configure(c *config.Config, rel string, f *rule.File) { } // Collect the ignore files for this package - kt.gitignore.CollectIgnoreFiles(c, rel) + git.CollectIgnoreFiles(c, rel) if f != nil { for _, d := range f.Directives { @@ -58,6 +62,10 @@ func (kt *kotlinLang) Configure(c *config.Config, rel string, f *rule.File) { case jvm_javaconfig.JavaMavenInstallFile: cfg.SetMavenInstallFile(d.Value) + + // TODO: move to common + case git.Directive_GitIgnore: + git.EnableGitignore(c, common.ReadEnabled(d)) } } } diff --git a/gazelle/kotlin/generate.go b/gazelle/kotlin/generate.go index 923e2a1df..b05a5e6ad 100644 --- a/gazelle/kotlin/generate.go +++ b/gazelle/kotlin/generate.go @@ -218,7 +218,7 @@ func (kt *kotlinLang) collectSourceFiles(cfg *kotlinconfig.KotlinConfig, args la // TODO: "module" targets similar to java? - gazelle.GazelleWalkDir(args, kt.gitignore.Matches, func(f string) error { + gazelle.GazelleWalkDir(args, func(f string) error { // Otherwise the file is either source or potentially importable. if isSourceFileType(f) { BazelLog.Tracef("SourceFile: %s", f) diff --git a/gazelle/kotlin/language.go b/gazelle/kotlin/language.go index f53b3e83b..be936cd67 100644 --- a/gazelle/kotlin/language.go +++ b/gazelle/kotlin/language.go @@ -1,7 +1,6 @@ package gazelle import ( - "aspect.build/cli/gazelle/common/git" jvm_maven "github.com/bazel-contrib/rules_jvm/java/gazelle/private/maven" "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/language" @@ -25,9 +24,6 @@ var _ language.Language = (*kotlinLang)(nil) // TypeScript satisfies the language.Language interface including the // Configurer and Resolver types. type kotlinLang struct { - // Ignore configurations for the workspace. - gitignore *git.GitIgnore - // TODO: extend rules_jvm extension instead of duplicating? mavenResolver *jvm_maven.Resolver mavenInstallFile string @@ -36,9 +32,7 @@ type kotlinLang struct { // NewLanguage initializes a new TypeScript that satisfies the language.Language // interface. This is the entrypoint for the extension initialization. func NewLanguage() language.Language { - return &kotlinLang{ - gitignore: git.NewGitIgnore(), - } + return &kotlinLang{} } var kotlinKinds = map[string]rule.KindInfo{ diff --git a/gazelle/kotlin/tests/ignore_config_files/BUILD.in b/gazelle/kotlin/tests/ignore_config_files/BUILD.in index e69de29bb..b5548bc47 100644 --- a/gazelle/kotlin/tests/ignore_config_files/BUILD.in +++ b/gazelle/kotlin/tests/ignore_config_files/BUILD.in @@ -0,0 +1 @@ +# gazelle:gitignore enabled diff --git a/gazelle/kotlin/tests/ignore_config_files/BUILD.out b/gazelle/kotlin/tests/ignore_config_files/BUILD.out index 5f3ad8684..92dd716ce 100644 --- a/gazelle/kotlin/tests/ignore_config_files/BUILD.out +++ b/gazelle/kotlin/tests/ignore_config_files/BUILD.out @@ -1,5 +1,7 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +# gazelle:gitignore enabled + kt_jvm_library( name = "ignore_config_files", srcs = [ diff --git a/patches/bazelbuild_bazel-gazelle_aspect-gitignore.patch b/patches/bazelbuild_bazel-gazelle_aspect-gitignore.patch new file mode 100644 index 000000000..6d010a045 --- /dev/null +++ b/patches/bazelbuild_bazel-gazelle_aspect-gitignore.patch @@ -0,0 +1,42 @@ +diff --git a/config/config.go b/config/config.go +index 23b1083..1841650 100644 +--- a/config/config.go ++++ b/config/config.go +@@ -121,6 +121,7 @@ type MappedKind struct { + } + + const ASPECT_WALKSUBDIR = "__aspect:walksubdir" ++const ASPECT_GITIGNORE = "__aspect:gitignore" + + func New() *Config { + return &Config{ +diff --git a/walk/walk.go b/walk/walk.go +index 7814088..8ebb086 100644 +--- a/walk/walk.go ++++ b/walk/walk.go +@@ -165,6 +165,13 @@ func visit(c *config.Config, cexts []config.Configurer, knownDirectives map[stri + return nil, false + } + ++ // PATCH(gitignore) --- ++ isGitIgnored, hasGitIgnore := c.Exts[config.ASPECT_GITIGNORE].(isIgnoredFunc) ++ if !hasGitIgnore || isGitIgnored == nil { ++ isGitIgnored = nothingIgnored ++ } ++ // END PATCH(gitignore) --- ++ + var dirs, subdirs, regularFiles []string + for _, ent := range ents { + base := ent.Name() +@@ -172,6 +179,11 @@ func visit(c *config.Config, cexts []config.Configurer, knownDirectives map[stri + if wc.isExcluded(entRel) { + continue + } ++ // PATCH(gitignore) --- ++ if isGitIgnored(entRel) { ++ continue ++ } ++ // END PATCH(gitignore) --- + ent := resolveFileInfo(wc, dir, entRel, ent) + switch { + case ent == nil: