parser

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2025 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package parser provides test file scanning and parsing capabilities. It supports multiple test frameworks (Jest, Vitest, Playwright, Go testing) and enables parallel processing for efficient scanning of large codebases.

Example
package main

import (
	"context"
	"fmt"

	"github.com/specvital/core/pkg/parser"

	// Import strategies to register them with the default registry.
	_ "github.com/specvital/core/pkg/parser/strategies/gotesting"
	_ "github.com/specvital/core/pkg/parser/strategies/jest"
	_ "github.com/specvital/core/pkg/parser/strategies/playwright"
	_ "github.com/specvital/core/pkg/parser/strategies/vitest"
)

func main() {
	ctx := context.Background()

	// Scan a project directory for test files
	result, err := parser.Scan(ctx, "/path/to/project")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	// Print discovered test files
	for _, file := range result.Inventory.Files {
		fmt.Printf("File: %s (framework: %s)\n", file.Path, file.Framework)
		fmt.Printf("  Tests: %d\n", file.CountTests())
	}

	// Check for non-fatal errors
	for _, scanErr := range result.Errors {
		fmt.Printf("Warning: %v\n", scanErr)
	}
}
Example (WithOptions)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/specvital/core/pkg/parser"

	// Import strategies to register them with the default registry.
	_ "github.com/specvital/core/pkg/parser/strategies/gotesting"
	_ "github.com/specvital/core/pkg/parser/strategies/jest"
	_ "github.com/specvital/core/pkg/parser/strategies/playwright"
	_ "github.com/specvital/core/pkg/parser/strategies/vitest"
)

func main() {
	ctx := context.Background()

	// Scan with custom options
	result, err := parser.Scan(ctx, "/path/to/project",
		parser.WithWorkers(4),                             // Use 4 parallel workers
		parser.WithTimeout(2*time.Minute),                 // Set 2 minute timeout
		parser.WithExclude([]string{"fixtures"}),          // Skip fixtures directory
		parser.WithScanPatterns([]string{"**/*.test.ts"}), // Only *.test.ts files
	)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Found %d test files\n", len(result.Inventory.Files))
}

Index

Examples

Constants

View Source
const (
	// DefaultWorkers indicates that the scanner should use GOMAXPROCS as the worker count.
	DefaultWorkers = 0
	// DefaultTimeout is the default scan timeout duration.
	DefaultTimeout = 5 * time.Minute
	// MaxWorkers is the maximum number of concurrent workers allowed.
	MaxWorkers = 1024
)
View Source
const (
	// DefaultMaxFileSize is the default maximum file size for detection (10MB).
	DefaultMaxFileSize = 10 * 1024 * 1024 // 10MB

)
View Source
const MaxTreeDepth = 1000

MaxTreeDepth is the maximum recursion depth when walking AST trees.

Variables

View Source
var (
	// ErrScanCancelled is returned when scanning is cancelled via context.
	ErrScanCancelled = errors.New("scanner: scan cancelled")
	// ErrScanTimeout is returned when scanning exceeds the timeout duration.
	ErrScanTimeout = errors.New("scanner: scan timeout")
)
View Source
var DefaultSkipPatterns = []string{
	"node_modules",
	".git",
	"vendor",
	"dist",
	".next",
	"__pycache__",
	"coverage",
	".cache",
}

DefaultSkipPatterns contains directory names that are skipped by default during detection.

View Source
var ErrInvalidRootPath = errors.New("detector: root path does not exist or is not accessible")

ErrInvalidRootPath is returned when the root path does not exist or is not a directory.

Functions

func ClearQueryCache

func ClearQueryCache()

ClearQueryCache removes all cached queries. Only for testing.

func FindChildByType

func FindChildByType(node *sitter.Node, nodeType string) *sitter.Node

FindChildByType returns the first direct child with the given node type.

func FindChildrenByType

func FindChildrenByType(node *sitter.Node, nodeType string) []*sitter.Node

FindChildrenByType returns all direct children with the given node type.

func GetLocation

func GetLocation(node *sitter.Node, filename string) domain.Location

GetLocation converts a tree-sitter node position to a domain.Location. Line numbers are converted to 1-based indexing.

func GetNodeText

func GetNodeText(node *sitter.Node, source []byte) string

GetNodeText returns the source text for the given AST node.

func ParseWithPool

func ParseWithPool(ctx context.Context, lang domain.Language, source []byte) (*sitter.Tree, error)

ParseWithPool parses source using a pooled parser. Caller must close the returned tree.

func WalkTree

func WalkTree(node *sitter.Node, visitor func(*sitter.Node) bool)

WalkTree recursively visits all nodes in the AST. The visitor function returns false to stop traversing into children.

Types

type DetectionResult

type DetectionResult struct {
	// Errors contains non-fatal errors encountered during directory traversal.
	Errors []error
	// Files contains paths to detected test files.
	Files []string
}

DetectionResult contains detected test files and any errors encountered during traversal.

func DetectTestFiles

func DetectTestFiles(ctx context.Context, rootPath string, opts ...DetectorOption) (*DetectionResult, error)

DetectTestFiles walks the directory tree and detects test files. It identifies test files by their naming conventions:

  • JavaScript/TypeScript: *.test.ts, *.spec.ts, __tests__/*.ts
  • Go: *_test.go

The function continues on errors, collecting them in [DetectionResult.Errors].

Example
package main

import (
	"context"
	"fmt"

	"github.com/specvital/core/pkg/parser"

	// Import strategies to register them with the default registry.
	_ "github.com/specvital/core/pkg/parser/strategies/gotesting"
	_ "github.com/specvital/core/pkg/parser/strategies/jest"
	_ "github.com/specvital/core/pkg/parser/strategies/playwright"
	_ "github.com/specvital/core/pkg/parser/strategies/vitest"
)

func main() {
	ctx := context.Background()

	// Detect test files without parsing
	result, err := parser.DetectTestFiles(ctx, "/path/to/project")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Found %d test files:\n", len(result.Files))
	for _, path := range result.Files {
		fmt.Printf("  - %s\n", path)
	}
}
Example (WithPatterns)
package main

import (
	"context"
	"fmt"

	"github.com/specvital/core/pkg/parser"

	// Import strategies to register them with the default registry.
	_ "github.com/specvital/core/pkg/parser/strategies/gotesting"
	_ "github.com/specvital/core/pkg/parser/strategies/jest"
	_ "github.com/specvital/core/pkg/parser/strategies/playwright"
	_ "github.com/specvital/core/pkg/parser/strategies/vitest"
)

func main() {
	ctx := context.Background()

	// Detect only specific test files
	result, err := parser.DetectTestFiles(ctx, "/path/to/project",
		parser.WithPatterns([]string{"src/**/*.spec.ts"}),
		parser.WithMaxFileSize(5*1024*1024), // 5MB max
	)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Found %d matching test files\n", len(result.Files))
}

type DetectorOption

type DetectorOption func(*DetectorOptions)

DetectorOption is a functional option for configuring DetectTestFiles.

func WithMaxFileSize

func WithMaxFileSize(size int64) DetectorOption

WithMaxFileSize returns a DetectorOption that sets the maximum file size.

func WithPatterns

func WithPatterns(patterns []string) DetectorOption

WithPatterns returns a DetectorOption that filters files by glob patterns.

func WithSkipPatterns

func WithSkipPatterns(patterns []string) DetectorOption

WithSkipPatterns returns a DetectorOption that replaces the skip patterns.

type DetectorOptions

type DetectorOptions struct {
	// SkipPatterns specifies directory names to skip during detection.
	SkipPatterns []string
	// Patterns specifies glob patterns to filter test files.
	Patterns []string
	// MaxFileSize is the maximum file size in bytes to include.
	MaxFileSize int64
}

DetectorOptions configures the behavior of DetectTestFiles.

type QueryResult

type QueryResult struct {
	// Node is the first captured node in this match.
	Node *sitter.Node
	// Captures maps capture names to their corresponding nodes.
	Captures map[string]*sitter.Node
}

QueryResult contains the result of a tree-sitter query match.

func Query

func Query(root *sitter.Node, source []byte, lang domain.Language, queryStr string) ([]QueryResult, error)

Query executes a tree-sitter query and returns all matches. The query is compiled fresh each time; for repeated queries, use QueryWithCache.

func QueryWithCache

func QueryWithCache(root *sitter.Node, source []byte, lang domain.Language, queryStr string) ([]QueryResult, error)

QueryWithCache executes a query with cached compilation.

type ScanError

type ScanError struct {
	// Err is the underlying error.
	Err error
	// Path is the file path where the error occurred (may be empty for non-file errors).
	Path string
	// Phase indicates which phase the error occurred in ("detection" or "parsing").
	Phase string
}

ScanError represents an error that occurred during a specific phase of scanning.

func (ScanError) Error

func (e ScanError) Error() string

Error implements the error interface.

type ScanOption

type ScanOption func(*ScanOptions)

ScanOption is a functional option for configuring Scan.

func WithExclude

func WithExclude(patterns []string) ScanOption

WithExclude returns a ScanOption that adds directory patterns to skip. These patterns are matched against directory base names.

func WithScanMaxFileSize

func WithScanMaxFileSize(size int64) ScanOption

WithScanMaxFileSize returns a ScanOption that sets the maximum file size. Files larger than this size are skipped. Negative values are ignored.

func WithScanPatterns

func WithScanPatterns(patterns []string) ScanOption

WithScanPatterns returns a ScanOption that filters files by glob patterns. Only files matching at least one pattern are processed. Uses doublestar syntax (e.g., "**/*.test.ts", "src/**/*.spec.js").

func WithTimeout

func WithTimeout(d time.Duration) ScanOption

WithTimeout returns a ScanOption that sets the scan timeout. Negative values are ignored.

func WithWorkers

func WithWorkers(n int) ScanOption

WithWorkers returns a ScanOption that sets the number of parallel workers. Zero uses GOMAXPROCS, negative values are ignored.

type ScanOptions

type ScanOptions struct {
	// ExcludePatterns specifies directory names to skip during scanning.
	ExcludePatterns []string
	// MaxFileSize is the maximum file size in bytes to process.
	MaxFileSize int64
	// Patterns specifies glob patterns to match test files (e.g., "**/*.test.ts").
	Patterns []string
	// Timeout is the maximum duration for the entire scan operation.
	Timeout time.Duration
	// Workers is the number of concurrent file parsers.
	Workers int
}

ScanOptions configures the behavior of Scan.

type ScanResult

type ScanResult struct {
	// Errors contains non-fatal errors encountered during scanning.
	Errors []ScanError
	// Inventory contains all parsed test files.
	Inventory *domain.Inventory
}

ScanResult contains the outcome of a scan operation.

func Scan

func Scan(ctx context.Context, rootPath string, opts ...ScanOption) (*ScanResult, error)

Scan scans the given directory for test files and parses them. It uses parallel processing with configurable worker count and timeout. The function continues even when individual files fail to parse, collecting errors in [ScanResult.Errors].

Example (TestInventory)
package main

import (
	"context"
	"fmt"

	"github.com/specvital/core/pkg/parser"

	// Import strategies to register them with the default registry.
	_ "github.com/specvital/core/pkg/parser/strategies/gotesting"
	_ "github.com/specvital/core/pkg/parser/strategies/jest"
	_ "github.com/specvital/core/pkg/parser/strategies/playwright"
	_ "github.com/specvital/core/pkg/parser/strategies/vitest"
)

func main() {
	ctx := context.Background()

	result, err := parser.Scan(ctx, "/path/to/project")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	inv := result.Inventory
	fmt.Printf("Project: %s\n", inv.RootPath)
	fmt.Printf("Total test files: %d\n", len(inv.Files))
	fmt.Printf("Total tests: %d\n", inv.CountTests())

	// Iterate test structure
	for _, file := range inv.Files {
		fmt.Printf("\n%s (%s):\n", file.Path, file.Framework)

		// Top-level tests
		for _, test := range file.Tests {
			fmt.Printf("  - %s [%d:%d]\n", test.Name, test.Location.StartLine, test.Location.EndLine)
		}

		// Test suites
		for _, suite := range file.Suites {
			fmt.Printf("  %s:\n", suite.Name)
			for _, test := range suite.Tests {
				fmt.Printf("    - %s\n", test.Name)
			}
		}
	}
}

type TSParser

type TSParser struct {
	// contains filtered or unexported fields
}

TSParser wraps a tree-sitter parser for a specific language.

func NewTSParser

func NewTSParser(lang domain.Language) *TSParser

NewTSParser creates a new tree-sitter parser for the given language.

func (*TSParser) Parse

func (p *TSParser) Parse(ctx context.Context, source []byte) (*sitter.Tree, error)

Parse parses the source code and returns the AST tree.

Directories

Path Synopsis
Package detection provides hierarchical test framework detection.
Package detection provides hierarchical test framework detection.
config
Package config provides scope-based configuration file resolution.
Package config provides scope-based configuration file resolution.
matchers
Package matchers provides framework-specific detection rules.
Package matchers provides framework-specific detection rules.
Package strategies provides the strategy pattern implementation for test file parsing.
Package strategies provides the strategy pattern implementation for test file parsing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL