Skip to content
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.24.10] - 2026-07-30

### Fixed
- Symbol-level AST diff no longer marks an unchanged declaration as changed when a *neighboring* declaration is edited or removed. Each symbol's body text was extracted starting at `stmt.Pos()`, which in the TS AST includes the symbol's **leading trivia** (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a `// SECTION` header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (`scanner.SkipTrivia`), excluding leading comments/blank lines from the compared body. Example: removing three unused `createSelector` exports from `bootstrap_selector.ts` previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

## [0.24.9] - 2026-07-30

### Changed
Expand Down Expand Up @@ -367,6 +372,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Multi-stage Docker build
- Automated vendor upgrade workflow

[0.24.10]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.24.9...v0.24.10
[0.24.9]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.24.8...v0.24.9
[0.24.8]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.24.7...v0.24.8
[0.24.7]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.24.6...v0.24.7
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.24.9
0.24.10
24 changes: 18 additions & 6 deletions internal/tsparse/tsparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"goodchanges/tsgo-vendor/pkg/ast"
"goodchanges/tsgo-vendor/pkg/core"
"goodchanges/tsgo-vendor/pkg/parser"
"goodchanges/tsgo-vendor/pkg/scanner"
)

type Import struct {
Expand Down Expand Up @@ -266,9 +267,20 @@ func extractExports(stmt *ast.Node, analysis *FileAnalysis) {
}
}

// stmtStartLine returns the 1-based line of a statement's first real token,
// skipping leading trivia (comments and blank lines). Using stmt.Pos() directly
// would put StartLine at the start of the leading comment/blank-line block, so
// that block rides along in the symbol's extracted body text. Deleting or editing
// a *preceding* sibling then re-attaches its trailing comment to this symbol and
// spuriously marks this (unchanged) symbol as changed in the AST diff.
func stmtStartLine(stmt *ast.Node, text string, lineMap []core.TextPos) int {
return posToLine(scanner.SkipTrivia(text, stmt.Pos()), lineMap)
}

func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileAnalysis) {
isExported := ast.HasSyntacticModifier(stmt, ast.ModifierFlagsExport)
isDefault := ast.HasSyntacticModifier(stmt, ast.ModifierFlagsDefault)
text := analysis.SourceFile.Text()

switch stmt.Kind {
case ast.KindFunctionDeclaration:
Expand All @@ -284,7 +296,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "function",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: exportName,
Expand All @@ -303,7 +315,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "class",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: exportName,
Expand All @@ -315,7 +327,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "interface",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: name,
Expand All @@ -328,7 +340,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "type",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: name,
Expand All @@ -341,7 +353,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "enum",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: name,
Expand All @@ -358,7 +370,7 @@ func extractDeclarations(stmt *ast.Node, lineMap []core.TextPos, analysis *FileA
analysis.Symbols = append(analysis.Symbols, SymbolDecl{
Name: name,
Kind: "variable",
StartLine: posToLine(stmt.Pos(), lineMap),
StartLine: stmtStartLine(stmt, text, lineMap),
EndLine: posToLine(stmt.End(), lineMap),
IsExported: isExported,
ExportName: name,
Expand Down
Loading