Merge branch 'main' into cf-1084-reject-unchanged-target-modifications

This commit is contained in:
mashraf-222 2026-04-28 16:35:07 +03:00 committed by GitHub
commit baf7970c16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
477 changed files with 119203 additions and 25521 deletions

45
.claude/hooks/bash-guard.sh Executable file
View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# PreToolUse hook: Block Bash calls that should use dedicated tools.
# Exit 0 = allow, Exit 2 = block (message on stderr).
INPUT=$(cat 2>/dev/null || true)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || true)
[ -z "$COMMAND" ] && exit 0
# Strip leading env vars (FOO=bar cmd ...) and whitespace to get the actual command
STRIPPED=$(echo "$COMMAND" | sed 's/^[[:space:]]*\([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]*\)*//')
FIRST_CMD=$(echo "$STRIPPED" | awk '{print $1}')
case "$FIRST_CMD" in
grep|egrep|fgrep|rg)
echo "BLOCKED: Use the Grep tool instead of \`$FIRST_CMD\`. It provides better output and permissions handling." >&2
exit 2
;;
find)
echo "BLOCKED: Use the Glob tool instead of \`find\`. Glob is faster and returns results sorted by modification time." >&2
exit 2
;;
cat|head|tail)
echo "BLOCKED: Use the Read tool instead of \`$FIRST_CMD\`. Read provides line numbers and supports images/PDFs." >&2
exit 2
;;
awk)
echo "BLOCKED: Use the Grep tool or Read tool instead of \`awk\`." >&2
exit 2
;;
sed)
if echo "$COMMAND" | grep -qE '(^|[[:space:]])sed[[:space:]]+-i'; then
echo "BLOCKED: Use the Edit tool instead of \`sed -i\`. Edit tracks changes properly." >&2
exit 2
fi
;;
esac
# echo with file redirection (echo "..." > file)
if echo "$STRIPPED" | grep -qE '^echo\b.*[[:space:]]>'; then
echo "BLOCKED: Use the Write tool instead of \`echo >\`. Write provides proper file creation." >&2
exit 2
fi
exit 0

47
.claude/hooks/post-compact.sh Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env bash
# PreCompact hook: Inject state preservation guidance before context compaction.
cd "$CLAUDE_PROJECT_DIR" 2>/dev/null || exit 0
STATE=""
BRANCH=$(git branch --show-current 2>/dev/null)
[ -n "$BRANCH" ] && STATE="${STATE}Branch: ${BRANCH}\n"
DIRTY=$(git status --porcelain 2>/dev/null)
if [ -n "$DIRTY" ]; then
COUNT=$(echo "$DIRTY" | wc -l | tr -d ' ')
STATE="${STATE}Uncommitted files (${COUNT}):\n${DIRTY}\n"
fi
UPSTREAM=$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null)
if [ -n "$UPSTREAM" ]; then
AHEAD=$(git rev-list --count "${UPSTREAM}..HEAD" 2>/dev/null)
[ "$AHEAD" -gt 0 ] 2>/dev/null && STATE="${STATE}Unpushed commits: ${AHEAD}\n"
fi
RECENT=$(git log --oneline -5 2>/dev/null)
[ -n "$RECENT" ] && STATE="${STATE}Recent commits:\n${RECENT}\n"
LATEST_HANDOFF=$(ls -t "$CLAUDE_PROJECT_DIR/.claude/handoffs/"*.md 2>/dev/null | head -1)
if [ -n "$LATEST_HANDOFF" ] && [ -f "$LATEST_HANDOFF" ]; then
HANDOFF_CONTENT=$(head -40 "$LATEST_HANDOFF" 2>/dev/null)
[ -n "$HANDOFF_CONTENT" ] && STATE="${STATE}\nHandoff context:\n${HANDOFF_CONTENT}\n"
fi
STATE="${STATE}\nProject conventions to preserve:\n"
STATE="${STATE}- Python 3.9+, uv for all tooling, ruff + mypy via prek\n"
STATE="${STATE}- Verification: uv run prek (single command for lint/format/types)\n"
STATE="${STATE}- Pre-push: uv run prek run --from-ref origin/<base>\n"
STATE="${STATE}- Conventional commits: fix:, feat:, refactor:, test:, chore:\n"
STATE="${STATE}- Result type: Success(value) / Failure(error), check with is_successful()\n"
STATE="${STATE}- Language singleton: set_current_language() / current_language()\n"
STATE="${STATE}- libcst for code transforms, ast for read-only analysis\n"
[ -z "$STATE" ] && exit 0
EXPANDED=$(printf '%b' "$STATE")
jq -n --arg msg "PRESERVE the following session state through compaction:
$EXPANDED" '{"systemMessage": $msg}'
exit 0

View file

@ -1,5 +1,4 @@
#!/usr/bin/env bash
# Everyone is on macOS so this should be fine, we don't account for Windows
set -euo pipefail
input=$(cat)
@ -10,6 +9,5 @@ if [[ -z "$file_path" || ! -f "$file_path" ]]; then
fi
if [[ "$file_path" == *.py ]]; then
# First run auto-fixes formatting; second run catches real lint errors
uv run prek --files "$file_path" 2>/dev/null || uv run prek --files "$file_path"
fi

25
.claude/hooks/require-read.sh Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# PreToolUse hook: Block Write/Edit on existing files that haven't been Read first.
# Exit 0 = allow, Exit 2 = block (message on stderr).
INPUT=$(cat 2>/dev/null || true)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)
[ -z "$FILE_PATH" ] && exit 0
# New files don't need prior reads
[ ! -f "$FILE_PATH" ] && exit 0
TRACKER="$CLAUDE_PROJECT_DIR/.claude/.read-tracker"
if [ ! -f "$TRACKER" ]; then
echo "BLOCKED: Read \`$(basename "$FILE_PATH")\` first before modifying it." >&2
exit 2
fi
if grep -qxF "$FILE_PATH" "$TRACKER"; then
exit 0
fi
echo "BLOCKED: Read \`$(basename "$FILE_PATH")\` first before modifying it." >&2
exit 2

50
.claude/hooks/status-line.sh Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Status line: derive context from git state.
input=$(cat)
project_dir=$(echo "$input" | jq -r '.workspace.project_dir')
user=$(whoami)
branch=$(git -C "$project_dir" branch --show-current 2>/dev/null)
changed=$(git -C "$project_dir" diff --name-only HEAD 2>/dev/null)
[ -z "$changed" ] && changed=$(git -C "$project_dir" diff --name-only 2>/dev/null)
[ -z "$changed" ] && changed=$(git -C "$project_dir" diff --name-only --cached 2>/dev/null)
if [ -n "$changed" ]; then
area=$(echo "$changed" | sed 's|/.*||' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')
else
area=""
fi
context=""
case "$area" in
codeflash)
subsystem=$(echo "$changed" | grep '^codeflash/' | sed 's|^codeflash/||; s|/.*||' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')
[ -n "$subsystem" ] && context="editing $subsystem" ;;
tests)
target=$(echo "$changed" | grep '^tests/' | sed 's|^tests/||; s|/.*||' | sort -u | head -1)
[ -n "$target" ] && context="testing $target" ;;
.claude)
context="configuring claude" ;;
esac
if [ -z "$context" ] && [ -n "$branch" ]; then
case "$branch" in
feat/*|cf-*) context="building: ${branch#feat/}" ;;
fix/*) context="fixing: ${branch#fix/}" ;;
refactor/*) context="refactoring: ${branch#refactor/}" ;;
test/*) context="testing: ${branch#test/}" ;;
chore/*) context="chore: ${branch#chore/}" ;;
esac
fi
dirty=""
if [ -n "$(git -C "$project_dir" status --porcelain 2>/dev/null)" ]; then
dirty=" *"
fi
status="$user | codeflash"
[ -n "$context" ] && status="$status | $context"
[ -n "$branch" ] && status="$status | $branch$dirty"
echo "$status"

11
.claude/hooks/track-read.sh Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# PostToolUse hook: Track Read calls for the require-read guard.
INPUT=$(cat 2>/dev/null || true)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)
[ -z "$FILE_PATH" ] && exit 0
TRACKER="$CLAUDE_PROJECT_DIR/.claude/.read-tracker"
grep -qxF "$FILE_PATH" "$TRACKER" 2>/dev/null || echo "$FILE_PATH" >> "$TRACKER"
exit 0

View file

@ -4,10 +4,11 @@
- **Python**: 3.9+ syntax
- **Package management**: Always use `uv`, never `pip`
- **Tooling**: Ruff for linting/formatting, mypy strict mode, prek for pre-commit checks
- **Comments**: Minimal - only explain "why", not "what"
- **Docstrings**: Do not add docstrings to new or changed code unless the user explicitly asks for them — not even one-liners. The codebase intentionally keeps functions self-documenting through clear naming and type annotations
- **Types**: Match the type annotation style of surrounding code — the codebase uses annotations, so add them in new code
- **Naming**: NEVER use leading underscores (`_function_name`) - Python has no true private functions, use public names
- **Comments**: Minimal only explain "why", not "what"
- **Docstrings**: Do not add docstrings unless the user explicitly asks
- **Types**: Match the type annotation style of surrounding code
- **Naming**: No leading underscores (`_function_name`) — Python has no true private functions
- **Paths**: Always use absolute paths
- **Encoding**: Always pass `encoding="utf-8"` to `open()`, `read_text()`, `write_text()`, etc. in new or changed code — Windows defaults to `cp1252` which breaks on non-ASCII content. Don't flag pre-existing code that lacks it unless you're already modifying that line.
- **Verification**: Use `uv run prek` to verify code — it handles ruff, ty, mypy in one pass. Don't run `ruff`, `mypy`, or `python -c "import ..."` separately; `prek` is the single verification command
- **Encoding**: Always pass `encoding="utf-8"` to `open()`, `read_text()`, `write_text()` in new or changed code
- **Verification**: Use `uv run prek` — it handles ruff, ty, mypy in one pass. Don't run them separately
- **Code transforms**: Use `libcst` for code modification/transformation. `ast` is acceptable for read-only analysis

View file

@ -0,0 +1,19 @@
# Debugging
## Root cause first
When encountering a bug, investigate the root cause. Don't patch symptoms. If you're about to add a try/except, a fallback default, or a defensive check — ask whether the real fix is upstream.
## Isolated testing
Prefer running individual test functions over full suites. Only run the full suite when explicitly asked or before pushing.
- Single function: `uv run pytest tests/test_foo.py::TestBar::test_baz -v`
- Single module: `uv run pytest tests/test_foo.py -v`
- Full suite: only when asked, or before `git push`
When debugging a specific endpoint or integration, test it directly instead of running the entire pipeline end-to-end.
## Subprocess failures
When a subprocess fails, always log stdout and stderr. "Exit code 1" with no output is useless.

View file

@ -1,19 +1,35 @@
# Git Commits & Pull Requests
# Git
## Commits
- Never commit, amend, or push without explicit permission
- Don't commit intermediate states — wait until the full implementation is complete, reviewed, and explicitly approved before committing. If the user corrects direction mid-implementation, incorporate the correction before any commit
- Always create a new branch from `main` before starting any new work — never commit directly to `main` or reuse an existing feature branch for unrelated changes
- Use conventional commit format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
- Keep commits atomic - one logical change per commit
- Commit message body should be concise (1-2 sentences max)
- Merge for simple syncs, rebase when branches have diverged significantly
- When committing to an external/third-party repo, follow that repo's own conventions for versioning, changelog, and CI
- Pre-commit: Run `uv run prek` before committing — fix any issues before creating the commit
- Pre-push: Run `uv run prek run --from-ref origin/<base>` to check all changed files against the PR base — this matches CI behavior and catches issues that per-commit prek misses. To detect the base branch: `gh pr view --json baseRefName -q .baseRefName 2>/dev/null || echo main`
- Don't commit intermediate states — wait until the full implementation is complete and approved
- Always create a new branch from `main` — never commit directly to `main`
- Conventional format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
- First line: imperative verb + what changed, under 72 characters
- Body for *why*, not *what* — the diff shows what changed
- One purpose per commit: a bug fix, a new function, a refactor — not all three
- A commit that adds a function also adds its tests and exports — that's one logical change
## Sizing
- Too small: renaming a variable in one commit, updating its references in another
- Right size: adding a function with its tests, `__init__` export, and usage update
- Too large: implementing an entire subsystem in one commit
## Pre-commit / Pre-push
- Pre-commit: Run `uv run prek` before committing
- Pre-push: Run `uv run prek run --from-ref origin/<base>` to check all changed files against the PR base
## Pull Requests
- PR titles should use conventional format
- Keep the PR body short and straight to the point
- PR titles use conventional format
- Keep the PR body short and to the point
- If related to a Linear issue, include `CF-#` in the body
- Branch naming: `cf-#-title` (lowercase, hyphenated), no other prefixes/suffixes
- Branch naming: `cf-#-title` (lowercase, hyphenated)
## Branch Hygiene
- Delete feature branches locally after merging (`git branch -d <branch>`)
- Use `/clean_gone` to prune local branches whose remote tracking branch has been deleted

5
.claude/rules/github.md Normal file
View file

@ -0,0 +1,5 @@
# GitHub Interactions
ALWAYS use MCP GitHub tools (`mcp__github__*`) for GitHub operations. Check for a matching MCP tool first — only fall back to `gh` via Bash when no MCP tool exists for the operation.
This also applies to other MCP-connected services (Linear, Granola). MCP first, CLI second.

View file

@ -6,8 +6,8 @@ paths:
# Language Support Patterns
- Current language is a module-level singleton in `languages/current.py` — use `set_current_language()` / `current_language()`, never pass language as a parameter through call chains
- Use `get_language_support(identifier)` from `languages/registry.py` to get a `LanguageSupport` instance — never import language classes directly
- New language support classes must use the `@register_language` decorator to register with the extension and language registries
- `languages/__init__.py` uses `__getattr__` for lazy imports to avoid circular dependencies — follow this pattern when adding new exports
- Prefer `LanguageSupport` protocol dispatch over `is_python()`/`is_javascript()` guards — remaining guards are being migrated to protocol methods
- Use `get_language_support(identifier)` from `languages/registry.py` — never import language classes directly
- New language support classes must use the `@register_language` decorator
- `languages/__init__.py` uses `__getattr__` for lazy imports to avoid circular dependencies
- Prefer `LanguageSupport` protocol dispatch over `is_python()`/`is_javascript()` guards
- `is_javascript()` returns `True` for both JavaScript and TypeScript (still used in ~15 call sites pending migration)

27
.claude/rules/sessions.md Normal file
View file

@ -0,0 +1,27 @@
# Session Discipline
## Scope
One task per session. Don't mix implementation with communication drafting, transcript search, or strategic planning.
## Duration
Cap sessions at 2-3 hours. Use `/handoff` at natural breakpoints rather than letting auto-compaction degrade context.
- After 1 compaction: consider wrapping up the current task and handing off
- After 3 compactions: stop, and tell the user to start a fresh session
- Never continue past 5 compactions — context is too degraded
## Context preservation
When compacting, preserve: modified files list, current branch, test commands used, key decisions made. Use subagents for exploration to keep main context clean.
## No polling
Never poll background tasks. No `wc -l`, no `tail -f`, no `sleep` loops. Use `run_in_background` and wait for the completion notification.
## File read budget
If you've read the same file 3+ times in a session, either:
- The session is too long and compaction destroyed your context — write a handoff
- You're not retaining key information — write it down in your response before it compacts away

View file

@ -1,8 +0,0 @@
---
paths:
- "codeflash/**/*.py"
---
# Source Code Rules
- Use `libcst` for code modification/transformation to preserve formatting. `ast` is acceptable for read-only analysis and parsing.

View file

@ -4,13 +4,14 @@ paths:
- "codeflash/**/*test*.py"
---
# Testing Conventions
# Testing
- Code context extraction and replacement tests must always assert for full string equality, no substring matching.
- Use pytest's `tmp_path` fixture for temp directories — do not use `tempfile.mkdtemp()`, `tempfile.TemporaryDirectory()`, or `NamedTemporaryFile`. Some existing tests still use `tempfile` but new tests must use `tmp_path`.
- Always call `.resolve()` on Path objects before passing them to functions under test — this ensures absolute paths and resolves symlinks. Example: `source_file = (tmp_path / "example.py").resolve()`
- Use `.as_posix()` when converting resolved paths to strings (normalizes to forward slashes).
- Any new feature or bug fix that can be tested automatically must have test cases.
- If changes affect existing test expectations, update the tests accordingly. Tests must always pass after changes.
- The pytest plugin patches `time`, `random`, `uuid`, and `datetime` for deterministic test execution — never assume real randomness or real time in verification tests.
- `conftest.py` uses an autouse fixture that calls `reset_current_language()` — tests always start with Python as the default language.
- Full string equality for context extraction/replacement tests — no substring matching
- Use pytest's `tmp_path` fixture — not `tempfile.mkdtemp()` or `NamedTemporaryFile`
- Always call `.resolve()` on Path objects before passing to functions under test
- Use `.as_posix()` when converting resolved paths to strings
- New features and bug fixes must have test cases
- The pytest plugin patches `time`, `random`, `uuid`, `datetime` for deterministic execution
- `conftest.py` autouse fixture calls `reset_current_language()` — tests start with Python as default
- Prefer running individual tests over full suites: `uv run pytest tests/test_foo.py::TestBar::test_baz -v`
- Only run the full suite when explicitly asked or before pushing

View file

@ -1,13 +1,17 @@
# Workflow
## Code Changes
- Before making any changes, outline your approach in 3-5 numbered steps. Include which repo/branch you'll work in, what commands you'll run, and what success looks like. Wait for approval before starting
Before making any changes, outline your approach in 3-5 numbered steps. Include which branch you'll work on, what commands you'll run, and what success looks like. Wait for approval before starting.
## Response Style
- When listing items (PRs, functions, optimization targets), always provide the complete list ordered by priority on the first attempt. Do not give partial lists
When listing items (PRs, functions, optimization targets), provide the complete list ordered by priority on the first attempt. No partial lists.
## Commands
- When running long-running commands (benchmarks, profiling, optimizers like codeflash), always run them in the foreground. Do not use background processes
Long-running commands (benchmarks, profiling, optimizers) always run in the foreground. Do not use background processes.
## Debugging
- When claiming something is a pre-existing issue (e.g., test failures on main), verify by checking out main and running the tests before making that claim
When claiming something is a pre-existing issue (e.g., test failures on main), verify by checking out main and running the tests before making that claim.

View file

@ -1,16 +1,89 @@
{
"attribution": {
"commit": "",
"pr": ""
},
"includeCoAuthoredBy": false,
"permissions": {
"allow": [
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git branch*)",
"Bash(git show*)",
"Bash(git fetch*)",
"Bash(git checkout*)",
"Bash(uv run*)",
"Bash(uv sync*)",
"Bash(uv pip*)",
"Bash(prek*)",
"Bash(make*)",
"Bash(gh *)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/bash-guard.sh",
"timeout": 5
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/require-read.sh",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/track-read.sh",
"timeout": 5
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/post-edit-lint.sh",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-edit-lint.sh",
"timeout": 30
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-compact.sh",
"timeout": 10
}
]
}
]
},
"statusLine": {
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/status-line.sh"
},
"enableAllProjectMcpServers": true,
"env": {
"ENABLE_LSP_TOOL": "1"
}
}

View file

@ -7,6 +7,6 @@ uv run mypy --non-interactive --config-file pyproject.toml <changed_files>
```
- Fix type annotation issues: missing return types, incorrect types, Optional/None unions, import errors for type hints
- Do NOT add `# type: ignore` comments always fix the root cause
- Do NOT add `# type: ignore` comments -- always fix the root cause
- Do NOT fix type errors that require logic changes, complex generic type rework, or anything that could change runtime behavior
- Files in `mypy_allowlist.txt` are checked in CI ensure they remain error-free
- Files in `mypy_allowlist.txt` are checked in CI -- ensure they remain error-free

View file

@ -5,5 +5,5 @@ When prek (pre-commit) checks fail:
1. Run `uv run prek run` to see failures (local, checks staged files)
2. In CI, the equivalent is `uv run prek run --from-ref origin/main`
3. prek runs ruff format, ruff check, and mypy on changed files
4. Fix issues in order: formatting → lint → type errors
4. Fix issues in order: formatting -> lint -> type errors
5. Re-run `uv run prek run` to verify all checks pass

View file

View file

@ -1,31 +1,18 @@
from argparse import Namespace
from pathlib import Path
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
from codeflash.languages.python.context.code_context_extractor import get_code_optimization_context
from codeflash.models.models import FunctionParent
from codeflash.optimization.optimizer import Optimizer
def test_benchmark_extract(benchmark) -> None:
file_path = Path(__file__).parent.parent.parent.resolve() / "codeflash"
opt = Optimizer(
Namespace(
project_root=file_path.resolve(),
disable_telemetry=True,
tests_root=(file_path / "tests").resolve(),
test_framework="pytest",
pytest_cmd="pytest",
experiment_id=None,
test_project_root=Path.cwd(),
)
)
project_root = Path(__file__).parent.parent.parent.resolve() / "codeflash"
function_to_optimize = FunctionToOptimize(
function_name="replace_function_and_helpers_with_optimized_code",
file_path=file_path / "languages" / "function_optimizer.py",
file_path=project_root / "languages" / "function_optimizer.py",
parents=[FunctionParent(name="FunctionOptimizer", type="ClassDef")],
starting_line=None,
ending_line=None,
)
benchmark(get_code_optimization_context, function_to_optimize, opt.args.project_root)
benchmark(get_code_optimization_context, function_to_optimize, project_root)

View file

@ -0,0 +1,133 @@
"""Benchmark comparator type dispatch performance.
Exercises the fast-path frozenset lookup vs isinstance MRO traversal
across realistic return value shapes: primitives, nested containers,
and mixed-type structures typical of real optimization verification.
"""
from __future__ import annotations
from collections import OrderedDict
from decimal import Decimal
from codeflash.verification.comparator import comparator
# --- Test data: realistic return value shapes ---
# 1. Flat primitives (int, bool, None, str, float, bytes) — the fast-path sweet spot
_PRIMITIVES_A = [
42,
True,
None,
3.14,
"hello",
b"bytes",
0,
False,
"",
1.0,
-1,
None,
True,
99,
"world",
b"\x00\x01",
2**31,
0.0,
False,
None,
]
_PRIMITIVES_B = list(_PRIMITIVES_A)
# 2. Nested dict of lists (common return value shape: API responses, parsed configs)
_NESTED_DICT_A = {
"users": [{"id": i, "name": f"user_{i}", "active": i % 2 == 0, "score": i * 1.5} for i in range(50)],
"metadata": {"total": 50, "page": 1, "has_next": True},
"tags": [f"tag_{i}" for i in range(20)],
"config": {"timeout": 30, "retries": 3, "debug": False, "threshold": Decimal("0.95")},
}
_NESTED_DICT_B = {
"users": [{"id": i, "name": f"user_{i}", "active": i % 2 == 0, "score": i * 1.5} for i in range(50)],
"metadata": {"total": 50, "page": 1, "has_next": True},
"tags": [f"tag_{i}" for i in range(20)],
"config": {"timeout": 30, "retries": 3, "debug": False, "threshold": Decimal("0.95")},
}
# 3. List of tuples (common: database rows, CSV data)
_ROWS_A = [(i, f"row_{i}", i * 0.1, i % 3 == 0, None if i % 5 == 0 else i) for i in range(200)]
_ROWS_B = [(i, f"row_{i}", i * 0.1, i % 3 == 0, None if i % 5 == 0 else i) for i in range(200)]
# 4. Deeply nested structure (worst case for recursive comparator)
def _make_deep(depth: int) -> dict:
if depth == 0:
return {"leaf": True, "value": 42, "items": [1, 2, 3], "label": "end"}
return {"level": depth, "child": _make_deep(depth - 1), "siblings": list(range(depth))}
_DEEP_A = _make_deep(15)
_DEEP_B = _make_deep(15)
# 5. Mixed identity types (frozenset, range, slice, OrderedDict, bytes, complex)
_IDENTITY_TYPES_A = [
frozenset({1, 2, 3}),
range(100),
complex(1, 2),
Decimal("3.14"),
OrderedDict(a=1, b=2),
b"binary",
bytearray(b"mutable"),
memoryview(b"view"),
type(None),
True,
42,
None,
] * 10
_IDENTITY_TYPES_B = list(_IDENTITY_TYPES_A)
def _compare_all_primitives() -> None:
for a, b in zip(_PRIMITIVES_A, _PRIMITIVES_B):
comparator(a, b)
def _compare_nested_dict() -> None:
comparator(_NESTED_DICT_A, _NESTED_DICT_B)
def _compare_rows() -> None:
comparator(_ROWS_A, _ROWS_B)
def _compare_deep() -> None:
comparator(_DEEP_A, _DEEP_B)
def _compare_identity_types() -> None:
for a, b in zip(_IDENTITY_TYPES_A, _IDENTITY_TYPES_B):
comparator(a, b)
def test_benchmark_comparator_primitives(benchmark) -> None:
"""20 flat primitive comparisons (int, bool, None, str, float, bytes)."""
benchmark(_compare_all_primitives)
def test_benchmark_comparator_nested_dict(benchmark) -> None:
"""Nested dict with 50-element user list, metadata, tags, config."""
benchmark(_compare_nested_dict)
def test_benchmark_comparator_rows(benchmark) -> None:
"""200 tuples of (int, str, float, bool, Optional[int])."""
benchmark(_compare_rows)
def test_benchmark_comparator_deep(benchmark) -> None:
"""15-level deep nested dict structure."""
benchmark(_compare_deep)
def test_benchmark_comparator_identity_types(benchmark) -> None:
"""120 frozenset/range/complex/Decimal/OrderedDict/bytes comparisons."""
benchmark(_compare_identity_types)

View file

@ -0,0 +1,75 @@
"""Benchmark libcst visitor performance across many files.
Exercises the visitor-heavy codepaths that benefit from the libcst dispatch
table cache: discover_functions + get_code_optimization_context on multiple
real source files.
"""
from __future__ import annotations
from pathlib import Path
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
from codeflash.languages.python.context.code_context_extractor import get_code_optimization_context
from codeflash.languages.python.support import PythonSupport
from codeflash.models.models import FunctionParent
# Real source files from the codeflash codebase, chosen for size and visitor diversity.
_CODEFLASH_ROOT = Path(__file__).parent.parent.parent.resolve() / "codeflash"
_SOURCE_FILES: list[Path] = [
_CODEFLASH_ROOT / "languages" / "function_optimizer.py",
_CODEFLASH_ROOT / "languages" / "python" / "context" / "code_context_extractor.py",
_CODEFLASH_ROOT / "languages" / "python" / "support.py",
_CODEFLASH_ROOT / "languages" / "python" / "static_analysis" / "code_extractor.py",
_CODEFLASH_ROOT / "languages" / "python" / "static_analysis" / "code_replacer.py",
_CODEFLASH_ROOT / "code_utils" / "instrument_existing_tests.py",
_CODEFLASH_ROOT / "benchmarking" / "compare.py",
_CODEFLASH_ROOT / "models" / "models.py",
_CODEFLASH_ROOT / "discovery" / "discover_unit_tests.py",
_CODEFLASH_ROOT / "languages" / "base.py",
]
# For each file, pick one top-level function to extract context for.
# (class, function_name) — class=None means module-level.
_TARGETS: list[tuple[Path, str | None, str]] = [
(_SOURCE_FILES[0], "FunctionOptimizer", "replace_function_and_helpers_with_optimized_code"),
(_SOURCE_FILES[1], None, "get_code_optimization_context"),
(_SOURCE_FILES[2], "PythonSupport", "discover_functions"),
(_SOURCE_FILES[3], None, "add_global_assignments"),
(_SOURCE_FILES[4], None, "replace_functions_in_file"),
(_SOURCE_FILES[5], None, "inject_profiling_into_existing_test"),
(_SOURCE_FILES[6], None, "compare_branches"),
(_SOURCE_FILES[7], None, "get_comment_prefix"),
(_SOURCE_FILES[8], None, "discover_unit_tests"),
(_SOURCE_FILES[9], None, "convert_parents_to_tuple"),
]
def _discover_all() -> None:
"""Run discover_functions on all source files."""
ps = PythonSupport()
for file_path in _SOURCE_FILES:
source = file_path.read_text(encoding="utf-8")
ps.discover_functions(source=source, file_path=file_path)
def _extract_all_contexts() -> None:
"""Run get_code_optimization_context on every target function."""
project_root = _CODEFLASH_ROOT.parent
for file_path, class_name, func_name in _TARGETS:
parents = [FunctionParent(name=class_name, type="ClassDef")] if class_name else []
fto = FunctionToOptimize(
function_name=func_name, file_path=file_path, parents=parents, starting_line=None, ending_line=None
)
get_code_optimization_context(fto, project_root)
def test_benchmark_discover_functions_multi_file(benchmark) -> None:
"""Discover functions across 10 source files."""
benchmark(_discover_all)
def test_benchmark_extract_context_multi_file(benchmark) -> None:
"""Extract code optimization context for 10 functions across 10 files."""
benchmark(_extract_all_contexts)

View file

@ -0,0 +1,56 @@
"""Benchmark the full libcst-heavy pipeline on a single file.
Runs discover extract context replace functions add global assignments
in sequence, exercising ~15 distinct visitor/transformer classes in one pass.
"""
from __future__ import annotations
from pathlib import Path
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
from codeflash.languages.python.context.code_context_extractor import get_code_optimization_context
from codeflash.languages.python.static_analysis.code_extractor import add_global_assignments
from codeflash.languages.python.static_analysis.code_replacer import replace_functions_in_file
from codeflash.languages.python.support import PythonSupport
_CODEFLASH_ROOT = Path(__file__).parent.parent.parent.resolve() / "codeflash"
_PROJECT_ROOT = _CODEFLASH_ROOT.parent
# Target: a real, non-trivial file with classes and module-level functions.
_TARGET_FILE = _CODEFLASH_ROOT / "languages" / "python" / "static_analysis" / "code_extractor.py"
_TARGET_FUNC = "add_global_assignments"
# A second file to serve as "optimized" source for replace/merge steps.
_SECOND_FILE = _CODEFLASH_ROOT / "languages" / "python" / "static_analysis" / "code_replacer.py"
def _run_pipeline() -> None:
"""Simulate a single-file optimization pass through the full visitor pipeline."""
source = _TARGET_FILE.read_text(encoding="utf-8")
source2 = _SECOND_FILE.read_text(encoding="utf-8")
# 1. Discover functions (FunctionVisitor + MetadataWrapper)
ps = PythonSupport()
functions = ps.discover_functions(source=source, file_path=_TARGET_FILE)
# 2. Extract code optimization context (multiple collectors + dependency resolver)
fto = FunctionToOptimize(
function_name=_TARGET_FUNC, file_path=_TARGET_FILE, parents=[], starting_line=None, ending_line=None
)
get_code_optimization_context(fto, _PROJECT_ROOT)
# 3. Replace functions (GlobalFunctionCollector + GlobalFunctionTransformer)
# Use a class method from discovered functions if available, else module-level.
func_names = [_TARGET_FUNC]
replace_functions_in_file(
source_code=source, original_function_names=func_names, optimized_code=source2, preexisting_objects=set()
)
# 4. Add global assignments (6 visitors/transformers)
add_global_assignments(source2, source)
def test_benchmark_full_pipeline(benchmark) -> None:
"""Full discover → extract → replace → merge pipeline on one file."""
benchmark(_run_pipeline)

View file

@ -2,7 +2,7 @@ from codeflash.models.models import FunctionTestInvocation, InvocationId, TestRe
from codeflash.verification.parse_test_output import merge_test_results
def generate_test_invocations(count=100):
def generate_test_invocations(count: int = 100) -> tuple[TestResults, TestResults]:
"""Generate a set number of test invocations for benchmarking."""
test_results_xml = TestResults()
test_results_bin = TestResults()
@ -21,7 +21,7 @@ def generate_test_invocations(count=100):
function_getting_tested="sorter",
iteration_id=iteration_id,
),
file_name="/tmp/tests/unittest/test_bubble_sort__perfinstrumented.py",
file_name="/tmp/tests/unittest/test_bubble_sort__perfinstrumented.py", # noqa: S108
did_pass=True,
runtime=None if i % 3 == 0 else i * 100, # Vary runtime values
test_framework="unittest",
@ -42,7 +42,7 @@ def generate_test_invocations(count=100):
function_getting_tested="sorter",
iteration_id=iteration_id,
),
file_name="/tmp/tests/unittest/test_bubble_sort__perfinstrumented.py",
file_name="/tmp/tests/unittest/test_bubble_sort__perfinstrumented.py", # noqa: S108
did_pass=True,
runtime=500 + i * 20, # Generate varying runtime values
test_framework="unittest",
@ -56,12 +56,12 @@ def generate_test_invocations(count=100):
return test_results_xml, test_results_bin
def run_merge_benchmark(count=100):
def run_merge_benchmark(count: int = 100) -> None:
test_results_xml, test_results_bin = generate_test_invocations(count)
# Perform the merge operation that will be benchmarked
merge_test_results(xml_test_results=test_results_xml, bin_test_results=test_results_bin, test_framework="unittest")
def test_benchmark_merge_test_results(benchmark):
def test_benchmark_merge_test_results(benchmark) -> None:
benchmark(run_merge_benchmark, 1000) # Default to 100 test invocations

17
.coveragerc Normal file
View file

@ -0,0 +1,17 @@
[run]
branch = true
source = codeflash
omit =
codeflash/version.py
[report]
sort = cover
show_missing = true
fail_under = 58
exclude_lines =
pragma: no cover
if TYPE_CHECKING:
if __name__ == .__main__.:
[html]
directory = htmlcov

38
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,38 @@
# Default fallback
* @KRRT7
# Java
/codeflash/languages/java/ @mashraf-222 @HeshamHM28 @misrasaurabh1
# JavaScript / TypeScript
/codeflash/languages/javascript/ @Saga4 @mohammedahmed18 @KRRT7
# Python language support
/codeflash/languages/python/ @KRRT7
# Core pipeline
/codeflash/optimization/ @KRRT7 @aseembits93 @misrasaurabh1
/codeflash/verification/ @KRRT7 @misrasaurabh1
/codeflash/benchmarking/ @KRRT7
/codeflash/discovery/ @KRRT7 @misrasaurabh1
# CLI & setup
/codeflash/cli_cmds/ @KRRT7 @misrasaurabh1
# LSP
/codeflash/lsp/ @mohammedahmed18
# API
/codeflash/api/ @KRRT7 @aseembits93
# Tracing & entry points
/codeflash/tracing/ @misrasaurabh1 @KRRT7
/codeflash/main.py @misrasaurabh1 @KRRT7
/codeflash/tracer.py @misrasaurabh1 @KRRT7
# Shared utilities
/codeflash/code_utils/ @KRRT7 @aseembits93 @misrasaurabh1
/codeflash/models/ @KRRT7
# CI / workflows
/.github/ @KRRT7

18
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,18 @@
## Linked issue or discussion
<!-- Every PR must link to an issue or discussion — this ensures the approach has been discussed with maintainers before implementation begins, so your work fits the project's direction and doesn't need to be reworked. -->
<!-- Replace the line below with one of: -->
<!-- Closes #<number> -->
<!-- Fixes #<number> -->
<!-- Relates to #<number> -->
<!-- Discussion: <url> -->
**Required:** <!-- CI will fail if no linked issue or discussion is found. -->
## What changed
<!-- Brief description of the changes. -->
## Test plan
<!-- How was this tested? Link to passing CI, new tests, or manual verification steps. -->

35
.github/actions/validate-pr/action.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Validate PR
description: Ensure only authorized users can modify workflow files in PRs
inputs:
base_sha:
description: Base commit SHA of the pull request
required: true
head_sha:
description: Head commit SHA of the pull request
required: true
author:
description: Login of the PR author
required: true
pr_state:
description: State of the pull request (open/closed)
required: true
runs:
using: composite
steps:
- name: Check workflow file changes
shell: bash
run: |
if git diff --name-only "${{ inputs.base_sha }}" "${{ inputs.head_sha }}" | grep -q "^.github/workflows/"; then
echo "Workflow changes detected."
AUTHOR="${{ inputs.author }}"
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ inputs.pr_state }}" == "open" ]]; then
echo "PR is open. Protection rules in place. Proceeding."
else
echo "Unauthorized user ($AUTHOR). Exiting."
exit 1
fi
else
echo "No workflow file changes. Proceeding."
fi

25
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,25 @@
version: 2
updates:
# Python (root pyproject.toml)
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
# JavaScript (codeflash npm package)
- package-ecosystem: "npm"
directory: "/packages/codeflash"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
# code_to_optimize/ directories are test fixtures — do NOT update them.
# Their package-lock.json files are gitignored to prevent Dependabot alerts.

537
.github/workflows/ci.yaml vendored Normal file
View file

@ -0,0 +1,537 @@
name: CI
on:
push:
branches: [main]
paths:
- 'codeflash/**'
- 'codeflash-benchmark/**'
- 'codeflash-java-runtime/**'
- 'tests/**'
- 'packages/**'
- 'pyproject.toml'
- 'uv.lock'
- 'mypy_allowlist.txt'
- '.github/workflows/ci.yaml'
- '.github/actions/**'
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# Linked issue check — every PR must reference an issue or discussion.
# Skipped on push to main and workflow_dispatch.
# ---------------------------------------------------------------------------
check-linked-issue:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- name: Check PR body for linked issue or discussion
env:
PR_BODY: ${{ github.event.pull_request.body }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
run: |
# Skip for bots (dependabot, renovate, github-actions)
if [[ "$PR_AUTHOR" == *"[bot]"* || "$PR_AUTHOR" == "dependabot" ]]; then
echo "Bot PR — skipping linked issue check."
exit 0
fi
# Skip for org members and collaborators
if [[ "$AUTHOR_ASSOCIATION" == "MEMBER" || "$AUTHOR_ASSOCIATION" == "COLLABORATOR" || "$AUTHOR_ASSOCIATION" == "OWNER" ]]; then
echo "Org member ($PR_AUTHOR, $AUTHOR_ASSOCIATION) — skipping linked issue check."
exit 0
fi
if [ -z "$PR_BODY" ]; then
echo "::error::PR body is empty. Every PR must link an issue or discussion."
echo "Use 'Closes #<number>', 'Fixes #<number>', 'Relates to #<number>', or include a discussion URL."
exit 1
fi
# Match: #123, GH-123, org/repo#123, Closes/Fixes/Relates/Resolves #123,
# or a github.com URL to an issue or discussion
if echo "$PR_BODY" | grep -qiP '(close[sd]?|fix(e[sd])?|relate[sd]?\s+to|resolve[sd]?)\s+#\d+'; then
echo "Found linked issue keyword."
exit 0
fi
if echo "$PR_BODY" | grep -qP '#\d+'; then
echo "Found issue reference."
exit 0
fi
if echo "$PR_BODY" | grep -qiP 'github\.com/[^\s]+/(issues|discussions)/\d+'; then
echo "Found GitHub issue/discussion URL."
exit 0
fi
if echo "$PR_BODY" | grep -qiP 'CF-#?\d+'; then
echo "Found Linear ticket reference."
exit 0
fi
echo "::error::No linked issue or discussion found in PR body."
echo "Every PR must reference an issue or discussion. See CONTRIBUTING.md for details."
echo "Use 'Closes #<number>', 'Fixes #<number>', 'Relates to #<number>', or include a discussion URL."
exit 1
# ---------------------------------------------------------------------------
# Change detection — decides which downstream jobs actually run.
# On push/workflow_dispatch every flag is true so all jobs execute.
# On pull_request we diff against the merge base.
# ---------------------------------------------------------------------------
determine-changes:
uses: codeflash-ai/github-workflows/.github/workflows/determine-changes.yml@main
with:
path-filters: |
{
"unit_tests": ["codeflash/", "codeflash-benchmark/", "tests/", "packages/", "pyproject.toml", "uv.lock"],
"type_check": ["codeflash/", "pyproject.toml", "uv.lock", "mypy_allowlist.txt"],
"e2e": ["codeflash/*.py", "codeflash/api/", "codeflash/benchmarking/", "codeflash/cli_cmds/", "codeflash/code_utils/", "codeflash/discovery/", "codeflash/github/", "codeflash/languages/python/", "codeflash/languages/*.py", "codeflash/lsp/", "codeflash/models/", "codeflash/optimization/", "codeflash/picklepatch/", "codeflash/result/", "codeflash/setup/", "codeflash/telemetry/", "codeflash/tracing/", "codeflash/verification/", "tests/", "pyproject.toml", "uv.lock"],
"e2e_js": ["codeflash/languages/javascript/", "codeflash/languages/base.py", "codeflash/languages/registry.py", "codeflash/optimization/", "codeflash/verification/", "packages/", "code_to_optimize/js/", "tests/scripts/end_to_end_test_js*"],
"e2e_java": ["codeflash/languages/java/", "codeflash/languages/base.py", "codeflash/languages/registry.py", "codeflash/optimization/", "codeflash/verification/", "codeflash-java-runtime/", "code_to_optimize/java/", "tests/scripts/end_to_end_test_java*", "tests/test_languages/fixtures/java_tracer_e2e/"]
}
# ---------------------------------------------------------------------------
# Unit tests — 6 Linux + 1 Windows matrix
# ---------------------------------------------------------------------------
unit-tests:
needs: determine-changes
if: fromJSON(needs.determine-changes.outputs.flags).unit_tests == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.9"
- os: ubuntu-latest
python-version: "3.10"
- os: ubuntu-latest
python-version: "3.11"
- os: ubuntu-latest
python-version: "3.12"
- os: ubuntu-latest
python-version: "3.13"
- os: ubuntu-latest
python-version: "3.14"
- os: windows-latest
python-version: "3.13"
runs-on: ${{ matrix.os }}
env:
PYTHONIOENCODING: utf-8
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Install dependencies
shell: bash
run: |
if [[ "${{ matrix.python-version }}" == "3.9" || "${{ matrix.python-version }}" == "3.13" ]]; then
uv sync --group tests
else
uv sync
fi
- name: Unit tests
run: uv run pytest tests/
# ---------------------------------------------------------------------------
# Coverage — single run on ubuntu/py3.13 to enforce the coverage floor.
# ---------------------------------------------------------------------------
coverage:
needs: determine-changes
if: fromJSON(needs.determine-changes.outputs.flags).unit_tests == 'true'
runs-on: ubuntu-latest
env:
PYTHONIOENCODING: utf-8
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: "3.13"
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Run tests with coverage
run: uv run pytest tests/ --ignore=tests/test_tracer.py --cov=codeflash --cov-report=xml:coverage.xml --cov-report=term-missing --cov-config=.coveragerc
- name: Check coverage floor
run: uv run coverage report --fail-under=58
- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v7
with:
name: coverage-report
path: coverage.xml
retention-days: 30
# ---------------------------------------------------------------------------
# Mypy type checking
# ---------------------------------------------------------------------------
type-check:
needs: determine-changes
if: fromJSON(needs.determine-changes.outputs.flags).type_check == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Run mypy
run: uv run mypy --non-interactive --config-file pyproject.toml @mypy_allowlist.txt
# ---------------------------------------------------------------------------
# Lint (prek) — pull_request only
# ---------------------------------------------------------------------------
prek:
needs: determine-changes
if: >-
github.event_name == 'pull_request'
&& (fromJSON(needs.determine-changes.outputs.flags).e2e == 'true'
|| fromJSON(needs.determine-changes.outputs.flags).e2e_js == 'true')
uses: codeflash-ai/github-workflows/.github/workflows/prek-lint.yml@main
permissions:
contents: write
with:
auto-fix: true
checkout-ref: ${{ github.head_ref }}
restore-paths: "codeflash/version.py codeflash-benchmark/codeflash_benchmark/version.py"
# ---------------------------------------------------------------------------
# E2E tests — only on pull_request and workflow_dispatch (not push to main)
# ---------------------------------------------------------------------------
# --- Standard Python E2Es (9 tests) ---
e2e-python:
needs: determine-changes
if: >-
fromJSON(needs.determine-changes.outputs.flags).e2e == 'true'
&& github.event_name != 'push'
&& github.actor != 'dependabot[bot]'
strategy:
fail-fast: false
matrix:
include:
- name: tracer-replay
script: end_to_end_test_tracer_replay.py
expected_improvement: 10
- name: bubble-sort-pytest-nogit
script: end_to_end_test_bubblesort_pytest.py
expected_improvement: 70
remove_git: true
- name: bubble-sort-unittest
script: end_to_end_test_bubblesort_unittest.py
expected_improvement: 40
- name: futurehouse-structure
script: end_to_end_test_futurehouse.py
expected_improvement: 5
- name: topological-sort
script: end_to_end_test_topological_sort_worktree.py
expected_improvement: 5
- name: async-optimization
script: end_to_end_test_async.py
expected_improvement: 10
- name: benchmark-bubble-sort
script: end_to_end_test_benchmark_sort.py
expected_improvement: 5
- name: coverage-e2e
script: end_to_end_test_coverage.py
extra_deps: black
- name: init-optimization
script: end_to_end_test_init_optimization.py
expected_improvement: 10
environment: ${{ ((github.event_name == 'workflow_dispatch' && github.actor != 'misrasaurabh1' && github.actor != 'KRRT7') || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
MAX_RETRIES: 3
RETRY_DELAY: 5
CODEFLASH_END_TO_END: 1
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref || '' }}
repository: ${{ github.event.pull_request.head.repo.full_name || '' }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
if: github.event_name == 'pull_request'
uses: ./.github/actions/validate-pr
with:
base_sha: ${{ github.event.pull_request.base.sha }}
head_sha: ${{ github.event.pull_request.head.sha }}
author: ${{ github.event.pull_request.user.login }}
pr_state: ${{ github.event.pull_request.state }}
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: 3.11.6
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Install extra dependencies
if: matrix.extra_deps
run: uv add ${{ matrix.extra_deps }}
- name: Set test configuration
if: matrix.expected_improvement
run: |
echo "COLUMNS=110" >> "$GITHUB_ENV"
echo "EXPECTED_IMPROVEMENT_PCT=${{ matrix.expected_improvement }}" >> "$GITHUB_ENV"
- name: Remove .git
if: matrix.remove_git
run: |
if [ -d ".git" ]; then
echo ".git directory exists!"
sudo rm -rf .git
if [ -d ".git" ]; then
echo ".git directory still exists after removal attempt!"
exit 1
else
echo ".git directory successfully removed."
fi
else
echo ".git directory does not exist. Nothing to remove."
exit 1
fi
- name: Run E2E test
run: uv run python tests/scripts/${{ matrix.script }}
# --- JS E2Es (3 tests, need Node.js + packages/) ---
e2e-js:
needs: determine-changes
if: >-
fromJSON(needs.determine-changes.outputs.flags).e2e_js == 'true'
&& github.event_name != 'push'
&& github.actor != 'dependabot[bot]'
strategy:
fail-fast: false
matrix:
include:
- name: js-cjs-function
script: end_to_end_test_js_cjs_function.py
js_project_dir: code_to_optimize/js/code_to_optimize_js
expected_improvement: 50
- name: js-esm-async
script: end_to_end_test_js_esm_async.py
js_project_dir: code_to_optimize/js/code_to_optimize_js_esm
expected_improvement: 10
allow_failure: true
- name: js-ts-class
script: end_to_end_test_js_ts_class.py
js_project_dir: code_to_optimize/js/code_to_optimize_ts
expected_improvement: 30
continue-on-error: ${{ matrix.allow_failure || false }}
environment: ${{ ((github.event_name == 'workflow_dispatch' && github.actor != 'misrasaurabh1' && github.actor != 'KRRT7') || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: ${{ matrix.expected_improvement }}
CODEFLASH_END_TO_END: 1
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref || '' }}
repository: ${{ github.event.pull_request.head.repo.full_name || '' }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
if: github.event_name == 'pull_request'
uses: ./.github/actions/validate-pr
with:
base_sha: ${{ github.event.pull_request.base.sha }}
head_sha: ${{ github.event.pull_request.head.sha }}
author: ${{ github.event.pull_request.user.login }}
pr_state: ${{ github.event.pull_request.state }}
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: |
packages/codeflash/package-lock.json
code_to_optimize/js/*/package-lock.json
- name: Install codeflash npm package dependencies
run: |
cd packages/codeflash
npm install
- name: Install JS test project dependencies
run: |
cd ${{ matrix.js_project_dir }}
npm install
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: 3.11.6
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Run E2E test
run: uv run python tests/scripts/${{ matrix.script }}
# --- Java E2Es (3 tests, need JDK + Maven) ---
e2e-java:
needs: determine-changes
if: >-
fromJSON(needs.determine-changes.outputs.flags).e2e_java == 'true'
&& github.event_name != 'push'
&& github.actor != 'dependabot[bot]'
strategy:
fail-fast: false
matrix:
include:
- name: java-fibonacci-nogit
script: end_to_end_test_java_fibonacci.py
expected_improvement: 70
remove_git: true
- name: java-tracer
script: end_to_end_test_java_tracer.py
expected_improvement: 10
- name: java-void-optimization-nogit
script: end_to_end_test_java_void_optimization.py
expected_improvement: 70
remove_git: true
environment: ${{ ((github.event_name == 'workflow_dispatch' && github.actor != 'misrasaurabh1' && github.actor != 'KRRT7') || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: ${{ matrix.expected_improvement }}
CODEFLASH_END_TO_END: 1
CODEFLASH_LOOPING_TIME: 5
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref || '' }}
repository: ${{ github.event.pull_request.head.repo.full_name || '' }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
if: github.event_name == 'pull_request'
uses: ./.github/actions/validate-pr
with:
base_sha: ${{ github.event.pull_request.base.sha }}
head_sha: ${{ github.event.pull_request.head.sha }}
author: ${{ github.event.pull_request.user.login }}
pr_state: ${{ github.event.pull_request.state }}
- name: Set up JDK 11
uses: actions/setup-java@v5
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: 3.11.6
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Cache codeflash-runtime JAR
id: runtime-jar-cache
uses: actions/cache@v5
with:
path: ~/.m2/repository/io/codeflash
key: codeflash-runtime-${{ hashFiles('codeflash-java-runtime/pom.xml', 'codeflash-java-runtime/src/**') }}
- name: Build and install codeflash-runtime JAR
if: steps.runtime-jar-cache.outputs.cache-hit != 'true'
run: |
cd codeflash-java-runtime
mvn install -q -DskipTests
- name: Remove .git
if: matrix.remove_git
run: |
if [ -d ".git" ]; then
sudo rm -rf .git
echo ".git directory removed."
else
echo ".git directory does not exist."
exit 1
fi
- name: Run E2E test
run: uv run python tests/scripts/${{ matrix.script }}
# ---------------------------------------------------------------------------
# Gate job — the ONLY required check in the GitHub ruleset.
# Accepts "success" and "skipped" (job skipped by change detection).
# Rejects "failure" and "cancelled".
# ---------------------------------------------------------------------------
required-checks-passed:
name: required checks passed
if: always()
needs:
- check-linked-issue
- unit-tests
- coverage
- type-check
- prek
- e2e-python
- e2e-js
- e2e-java
runs-on: ubuntu-latest
steps:
- uses: codeflash-ai/github-workflows/.github/actions/required-checks-gate@main
with:
needs-json: ${{ toJSON(needs) }}

View file

@ -27,17 +27,21 @@ on:
jobs:
# Automatic PR review (can fix linting issues and push)
# Blocked for fork PRs to prevent malicious code execution
# TEMPORARILY DISABLED — re-enable by removing `false &&` below
pr-review:
concurrency:
group: pr-review-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
if: |
false &&
(
github.event_name == 'pull_request' &&
github.event.sender.login != 'claude[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
) ||
github.event_name == 'workflow_dispatch'
(
github.event_name == 'pull_request' &&
github.event.sender.login != 'claude[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
) ||
github.event_name == 'workflow_dispatch'
)
runs-on: ubuntu-latest
permissions:
contents: write
@ -47,13 +51,15 @@ jobs:
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref || github.ref }}
- name: Install uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: true
- name: Install dependencies
run: |
@ -61,7 +67,7 @@ jobs:
uv sync
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}
@ -307,13 +313,15 @@ jobs:
fi
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ steps.pr-ref.outputs.ref }}
- name: Install uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: true
- name: Install dependencies
run: |
@ -321,7 +329,7 @@ jobs:
uv sync
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}

View file

@ -3,7 +3,10 @@ name: CodeFlash
on:
pull_request:
paths:
- '**' # Trigger for all paths
- 'codeflash/**'
- 'tests/**'
- 'pyproject.toml'
- 'uv.lock'
workflow_dispatch:
@ -23,14 +26,15 @@ jobs:
COLUMNS: 110
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: 🐍 Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: 3.11.6
enable-cache: true
- name: 📦 Install dependencies (CLI)
run: |
@ -39,4 +43,4 @@ jobs:
- name: Codeflash Optimization
id: optimize_code
run: |
uv run codeflash --benchmark --testgen-review
uv run codeflash --benchmark --testgen-review --no-pr

View file

@ -1,41 +0,0 @@
name: Codeflash
on:
pull_request:
paths:
# So that this workflow only runs when code within the target module is modified
- 'code_to_optimize_js_esm/**'
workflow_dispatch:
concurrency:
# Any new push to the PR will cancel the previous run, so that only the latest code is optimized
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
optimize:
name: Optimize new code
# Don't run codeflash on codeflash-ai[bot] commits, prevent duplicate optimizations
if: ${{ github.actor != 'codeflash-ai[bot]' }}
runs-on: ubuntu-latest
env:
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
defaults:
run:
working-directory: ./code_to_optimize_js_esm
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🟢 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: 📦 Install Dependencies
run: npm ci
- name: ⚡️ Codeflash Optimization
run: npx codeflash

View file

@ -1,73 +0,0 @@
name: E2E - Async
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
async-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize async code
id: optimize_async_code
run: |
uv run python tests/scripts/end_to_end_test_async.py

View file

@ -1,72 +0,0 @@
name: E2E - Bubble Sort Benchmark
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
benchmark-bubble-sort-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 5
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code_with_benchmarks
run: |
uv run python tests/scripts/end_to_end_test_benchmark_sort.py

View file

@ -1,88 +0,0 @@
name: E2E - Bubble Sort Pytest (No Git)
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
bubble-sort-optimization-pytest-no-git:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 70
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Remove .git
run: |
if [ -d ".git" ]; then
echo ".git directory exists!"
sudo rm -rf .git
if [ -d ".git" ]; then
echo ".git directory still exists after removal attempt!"
exit 1
else
echo ".git directory successfully removed."
fi
else
echo ".git directory does not exist. Nothing to remove."
exit 1
fi
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_bubblesort_pytest.py

View file

@ -1,72 +0,0 @@
name: E2E - Bubble Sort Unittest
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
bubble-sort-optimization-unittest:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 40
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_bubblesort_unittest.py

View file

@ -1,71 +0,0 @@
name: Coverage E2E
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
end-to-end-test-coverage:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
MAX_RETRIES: 3
RETRY_DELAY: 5
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
uv add black # my-best-repo in end_to_end_test_coverage.py is configured to use black
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_coverage.py

View file

@ -1,72 +0,0 @@
name: E2E - Futurehouse Structure
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
futurehouse-structure:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 5
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_futurehouse.py

View file

@ -1,71 +0,0 @@
name: E2E - Init Optimization
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
init-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_init_optimization.py

View file

@ -1,105 +0,0 @@
name: E2E - Java Fibonacci (No Git)
on:
pull_request:
paths:
- 'codeflash/languages/java/**'
- 'codeflash/languages/base.py'
- 'codeflash/languages/registry.py'
- 'codeflash/optimization/**'
- 'codeflash/verification/**'
- 'code_to_optimize/java/**'
- 'codeflash-java-runtime/**'
- 'tests/scripts/end_to_end_test_java_fibonacci.py'
- '.github/workflows/e2e-java-fibonacci-nogit.yaml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
java-fibonacci-optimization-no-git:
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 70
CODEFLASH_END_TO_END: 1
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
env:
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_STATE: ${{ github.event.pull_request.state }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
echo "PR Author: $PR_AUTHOR"
if [[ "$PR_AUTHOR" == "misrasaurabh1" || "$PR_AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($PR_AUTHOR). Proceeding."
elif [[ "$PR_STATE" == "open" ]]; then
echo "✅ PR is open. Proceeding."
else
echo "⛔ Unauthorized user ($PR_AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: uv sync
- name: Build codeflash-runtime JAR
run: |
cd codeflash-java-runtime
mvn clean package -q -DskipTests
mvn install -q -DskipTests
- name: Verify Java installation
run: |
java -version
mvn --version
- name: Remove .git
run: |
if [ -d ".git" ]; then
sudo rm -rf .git
echo ".git directory removed."
else
echo ".git directory does not exist."
exit 1
fi
- name: Run Codeflash to optimize Fibonacci
run: |
uv run python tests/scripts/end_to_end_test_java_fibonacci.py

View file

@ -1,90 +0,0 @@
name: E2E - Java Tracer
on:
pull_request:
paths:
- 'codeflash/**'
- 'codeflash-java-runtime/**'
- 'tests/**'
- '.github/workflows/e2e-java-tracer.yaml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
java-tracer-e2e:
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
env:
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_STATE: ${{ github.event.pull_request.state }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
echo "PR Author: $PR_AUTHOR"
if [[ "$PR_AUTHOR" == "misrasaurabh1" || "$PR_AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($PR_AUTHOR). Proceeding."
elif [[ "$PR_STATE" == "open" ]]; then
echo "✅ PR is open. Proceeding."
else
echo "⛔ Unauthorized user ($PR_AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: uv sync
- name: Build codeflash-runtime JAR
run: |
cd codeflash-java-runtime
mvn clean package -q -DskipTests
mvn install -q -DskipTests
- name: Verify Java installation
run: |
java -version
mvn --version
- name: Run Java tracer e2e test
run: |
uv run python tests/scripts/end_to_end_test_java_tracer.py

View file

@ -1,88 +0,0 @@
name: E2E - JS CommonJS Function
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
js-cjs-function-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 50
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install codeflash npm package dependencies
run: |
cd packages/codeflash
npm install
- name: Install JS test project dependencies
run: |
cd code_to_optimize/js/code_to_optimize_js
npm install
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize JS CommonJS function
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_js_cjs_function.py

View file

@ -1,88 +0,0 @@
name: E2E - JS ESM Async
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
js-esm-async-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install codeflash npm package dependencies
run: |
cd packages/codeflash
npm install
- name: Install JS test project dependencies
run: |
cd code_to_optimize/js/code_to_optimize_js_esm
npm install
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize ESM async function
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_js_esm_async.py

View file

@ -1,88 +0,0 @@
name: E2E - JS TypeScript Class
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
js-ts-class-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 30
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install codeflash npm package dependencies
run: |
cd packages/codeflash
npm install
- name: Install JS test project dependencies
run: |
cd code_to_optimize/js/code_to_optimize_ts
npm install
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize TypeScript class method
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_js_ts_class.py

View file

@ -1,97 +0,0 @@
name: E2E - Topological Sort (Worktree)
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
topological-sort-worktree-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 5
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Debug Environment Decision
run: |
# Construct the condition result manually for debugging
EVENT_NAME="${{ github.event_name }}"
FILES_CHANGED="${{ toJSON(github.event.pull_request.files.*.filename) }}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
echo "Event Name: $EVENT_NAME"
echo "Files Changed: $FILES_CHANGED"
echo "PR Author: $PR_AUTHOR"
# Check workflow file changes
if [[ "$FILES_CHANGED" == *".github/workflows/"* ]]; then
echo "Workflow files changed: YES"
else
echo "Workflow files changed: NO"
fi
# Check author conditions
if [[ "$PR_AUTHOR" != "misrasaurabh1" && "$PR_AUTHOR" != "KRRT7" ]]; then
echo "Author needs approval: YES"
else
echo "Author needs approval: NO"
fi
# Selected environment
echo "Selected Environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}"
- name: Validate PR for workflow changes
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_topological_sort_worktree.py

View file

@ -1,72 +0,0 @@
name: E2E - Tracer Replay
on:
pull_request:
paths:
- '**' # Trigger for all paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
tracer-replay:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."
# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"
# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi
- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11.6
- name: Install dependencies (CLI)
run: |
uv sync
- name: Run Codeflash to optimize code
id: optimize_code
run: |
uv run python tests/scripts/end_to_end_test_tracer_replay.py

View file

@ -1,76 +0,0 @@
name: Java E2E Tests
on:
push:
branches:
- main
- omni-java
paths:
- 'codeflash/languages/java/**'
- 'tests/test_languages/test_java*.py'
- 'code_to_optimize/java/**'
- '.github/workflows/java-e2e-tests.yml'
pull_request:
paths:
- 'codeflash/languages/java/**'
- 'tests/test_languages/test_java*.py'
- 'code_to_optimize/java/**'
- '.github/workflows/java-e2e-tests.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
java-e2e:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Set up Python environment
run: |
uv venv --seed
uv sync
- name: Verify Java installation
run: |
java -version
mvn --version
- name: Build codeflash-runtime JAR
run: |
cd codeflash-java-runtime
mvn clean package -q -DskipTests
mvn install -q -DskipTests
- name: Build Java sample project
run: |
cd code_to_optimize/java
mvn compile -q
- name: Run Java sample project tests
run: |
cd code_to_optimize/java
mvn test -q
- name: Run Java E2E tests
run: |
uv run pytest tests/test_languages/test_java_e2e.py -v --tb=short
- name: Run Java unit tests
run: |
uv run pytest tests/test_languages/test_java/ -v --tb=short -x

77
.github/workflows/java-e2e.yaml vendored Normal file
View file

@ -0,0 +1,77 @@
name: Java E2E Tests
on:
workflow_dispatch:
jobs:
e2e-java:
strategy:
fail-fast: false
matrix:
include:
- name: java-fibonacci-nogit
script: end_to_end_test_java_fibonacci.py
expected_improvement: 70
remove_git: true
- name: java-tracer
script: end_to_end_test_java_tracer.py
expected_improvement: 10
- name: java-void-optimization-nogit
script: end_to_end_test_java_void_optimization.py
expected_improvement: 70
remove_git: true
runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: ${{ matrix.expected_improvement }}
CODEFLASH_END_TO_END: 1
CODEFLASH_LOOPING_TIME: 5
steps:
- uses: actions/checkout@v6
- name: Set up JDK 11
uses: actions/setup-java@v5
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: 3.11.6
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Cache codeflash-runtime JAR
id: runtime-jar-cache
uses: actions/cache@v5
with:
path: ~/.m2/repository/io/codeflash
key: codeflash-runtime-${{ hashFiles('codeflash-java-runtime/pom.xml', 'codeflash-java-runtime/src/**') }}
- name: Build and install codeflash-runtime JAR
if: steps.runtime-jar-cache.outputs.cache-hit != 'true'
run: |
cd codeflash-java-runtime
mvn install -q -DskipTests
- name: Remove .git
if: matrix.remove_git
run: |
if [ -d ".git" ]; then
sudo rm -rf .git
echo ".git directory removed."
else
echo ".git directory does not exist."
exit 1
fi
- name: Run E2E test
run: uv run python tests/scripts/${{ matrix.script }}

View file

@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Label PR with workflow changes
uses: actions/github-script@v6
uses: actions/github-script@v9
with:
script: |
const labelName = 'workflow-modified';

View file

@ -1,33 +0,0 @@
name: Mypy Type Checking for CLI
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
type-check-cli:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: sync uv
run: |
uv venv --seed
uv sync
- name: Run mypy on allowlist
run: uv run mypy --non-interactive --config-file pyproject.toml @mypy_allowlist.txt

View file

@ -1,18 +0,0 @@
name: Lint
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
prek:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v6
- uses: j178/prek-action@v1
with:
extra-args: '--from-ref origin/${{ github.base_ref }} --to-ref ${{ github.sha }}'

View file

@ -16,7 +16,7 @@ jobs:
benchmark: ${{ steps.filter.outputs.benchmark }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 2
@ -34,9 +34,27 @@ jobs:
echo "benchmark=false" >> $GITHUB_OUTPUT
fi
publish-codeflash:
publish:
needs: detect-changes
if: needs.detect-changes.outputs.codeflash == 'true'
if: >-
needs.detect-changes.outputs.codeflash == 'true'
|| needs.detect-changes.outputs.benchmark == 'true'
strategy:
fail-fast: false
matrix:
include:
- package: codeflash
version_file: codeflash/version.py
tag_prefix: v
build_cmd: uv build
flag: codeflash
release_name_prefix: "Release"
- package: codeflash-benchmark
version_file: codeflash-benchmark/codeflash_benchmark/version.py
tag_prefix: benchmark-v
build_cmd: uv build --package codeflash-benchmark
flag: benchmark
release_name_prefix: "codeflash-benchmark"
runs-on: ubuntu-latest
environment:
name: pypi
@ -44,92 +62,34 @@ jobs:
id-token: write
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Extract version from version.py
id: extract_version
- name: Check if this matrix leg should run
id: should_run
run: |
VERSION=$(grep -oP '__version__ = "\K[^"]+' codeflash/version.py)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
- name: Check if tag already exists
id: check_tag
run: |
if git rev-parse "v${{ steps.extract_version.outputs.version }}" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.extract_version.outputs.version }} already exists, skipping release"
if [[ "${{ matrix.flag }}" == "codeflash" && "${{ needs.detect-changes.outputs.codeflash }}" == "true" ]]; then
echo "run=true" >> $GITHUB_OUTPUT
elif [[ "${{ matrix.flag }}" == "benchmark" && "${{ needs.detect-changes.outputs.benchmark }}" == "true" ]]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.extract_version.outputs.version }} does not exist, proceeding with release"
echo "run=false" >> $GITHUB_OUTPUT
fi
- name: Create and push git tag
if: steps.check_tag.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.extract_version.outputs.tag }}" -m "Release ${{ steps.extract_version.outputs.tag }}"
git push origin "${{ steps.extract_version.outputs.tag }}"
- name: Install uv
if: steps.check_tag.outputs.exists == 'false'
uses: astral-sh/setup-uv@v6
- name: Build
if: steps.check_tag.outputs.exists == 'false'
run: uv build
- name: Publish to PyPI
if: steps.check_tag.outputs.exists == 'false'
run: uv publish
- name: Create GitHub Release
if: steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.extract_version.outputs.tag }}
name: Release ${{ steps.extract_version.outputs.tag }}
body: |
## What's Changed
Release ${{ steps.extract_version.outputs.version }} of codeflash.
**Full Changelog**: https://github.com/${{ github.repository }}/commits/${{ steps.extract_version.outputs.tag }}
draft: false
prerelease: false
generate_release_notes: true
files: |
dist/*
publish-benchmark:
needs: detect-changes
if: needs.detect-changes.outputs.benchmark == 'true'
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5
if: steps.should_run.outputs.run == 'true'
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Extract version from version.py
if: steps.should_run.outputs.run == 'true'
id: extract_version
run: |
VERSION=$(grep -oP '__version__ = "\K[^"]+' codeflash-benchmark/codeflash_benchmark/version.py)
VERSION=$(grep -oP '__version__ = "\K[^"]+' ${{ matrix.version_file }})
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=benchmark-v$VERSION" >> $GITHUB_OUTPUT
echo "tag=${{ matrix.tag_prefix }}$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
- name: Check if tag already exists
if: steps.should_run.outputs.run == 'true'
id: check_tag
run: |
if git rev-parse "${{ steps.extract_version.outputs.tag }}" >/dev/null 2>&1; then
@ -141,7 +101,7 @@ jobs:
fi
- name: Create and push git tag
if: steps.check_tag.outputs.exists == 'false'
if: steps.should_run.outputs.run == 'true' && steps.check_tag.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
@ -149,31 +109,32 @@ jobs:
git push origin "${{ steps.extract_version.outputs.tag }}"
- name: Install uv
if: steps.check_tag.outputs.exists == 'false'
uses: astral-sh/setup-uv@v6
if: steps.should_run.outputs.run == 'true' && steps.check_tag.outputs.exists == 'false'
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: true
- name: Build
if: steps.check_tag.outputs.exists == 'false'
run: uv build --package codeflash-benchmark
if: steps.should_run.outputs.run == 'true' && steps.check_tag.outputs.exists == 'false'
run: ${{ matrix.build_cmd }}
- name: Publish to PyPI
if: steps.check_tag.outputs.exists == 'false'
if: steps.should_run.outputs.run == 'true' && steps.check_tag.outputs.exists == 'false'
run: uv publish
- name: Create GitHub Release
if: steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
if: steps.should_run.outputs.run == 'true' && steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.extract_version.outputs.tag }}
name: codeflash-benchmark ${{ steps.extract_version.outputs.tag }}
name: ${{ matrix.release_name_prefix }} ${{ steps.extract_version.outputs.tag }}
body: |
## What's Changed
Release ${{ steps.extract_version.outputs.version }} of codeflash-benchmark.
Release ${{ steps.extract_version.outputs.version }} of ${{ matrix.package }}.
**Full Changelog**: https://github.com/${{ github.repository }}/commits/${{ steps.extract_version.outputs.tag }}
draft: false
prerelease: false
generate_release_notes: true
files: |
dist/*

8
.github/workflows/ready-to-merge.yml vendored Normal file
View file

@ -0,0 +1,8 @@
name: Ready to Merge
on:
pull_request:
jobs:
ready:
uses: codeflash-ai/github-workflows/.github/workflows/ready-to-merge.yml@main

18
.github/workflows/tessl-update.yml vendored Normal file
View file

@ -0,0 +1,18 @@
name: Tessl Tile Updates
on:
schedule:
- cron: "0 9 * * 1" # Weekly on Monday at 9am UTC
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
tessl:
uses: codeflash-ai/github-workflows/.github/workflows/tessl-update.yml@main
secrets:
TESSL_TOKEN: ${{ secrets.TESSL_TOKEN }}
CI_BOT_APP_ID: ${{ secrets.CI_BOT_APP_ID }}
CI_BOT_PRIVATE_KEY: ${{ secrets.CI_BOT_PRIVATE_KEY }}

View file

@ -1,69 +0,0 @@
name: unit-tests
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
unit-tests:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.9"
- os: ubuntu-latest
python-version: "3.10"
- os: ubuntu-latest
python-version: "3.11"
- os: ubuntu-latest
python-version: "3.12"
- os: ubuntu-latest
python-version: "3.13"
- os: ubuntu-latest
python-version: "3.14"
- os: windows-latest
python-version: "3.13"
continue-on-error: true
runs-on: ${{ matrix.os }}
env:
PYTHONIOENCODING: utf-8
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build codeflash-runtime JAR
run: |
cd codeflash-java-runtime
mvn clean package -q -DskipTests
mvn install -q -DskipTests
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
- name: install dependencies
run: uv sync
- name: Install test-only dependencies (Python 3.9 and 3.13)
if: matrix.python-version == '3.9' || matrix.python-version == '3.13'
run: uv sync --group tests
- name: Unit tests
run: uv run pytest tests/

358
.gitignore vendored
View file

@ -1,7 +1,10 @@
# =============================================================================
# Python — https://github.com/github/gitignore/blob/main/Python.gitignore
# =============================================================================
# Byte-compiled / optimized / DLL files
__pycache__/
**/__pycache__/
*.py[cod]
*.py[codz]
*$py.class
# C extensions
@ -10,9 +13,8 @@ __pycache__/
# Distribution / packaging
.Python
build/
.gradle/
develop-eggs/
cli/dist/
dist/
downloads/
eggs/
.eggs/
@ -33,7 +35,6 @@ MANIFEST
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
*.pyc
# Installer logs
pip-log.txt
@ -49,7 +50,7 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
*.py,cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
@ -97,20 +98,35 @@ ipython_config.py
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
#poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
#pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
#pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
@ -124,7 +140,7 @@ celerybeat.pid
# Environments
.env
**/.env
.envrc
.venv
env/
venv/
@ -162,120 +178,254 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.aider*
/js/common/node_modules/
*.xml
# Allow pom.xml in test fixtures for Maven project detection
!tests/test_languages/fixtures/**/pom.xml
# Allow pom.xml in Java sample project
!code_to_optimize/java/pom.xml
# Allow pom.xml in codeflash-java-runtime
!codeflash-java-runtime/pom.xml
*.pem
# Ruff cache
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# IDE settings
.idea/
.vscode/
# PyPI configuration file
.pypirc
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
# AWS User-specific
.idea/**/aws.xml
# =============================================================================
# Node — https://github.com/github/gitignore/blob/main/Node.gitignore
# =============================================================================
# Generated files
.idea/**/contentModel.xml
# Logs
logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Sveltekit cache directory
.svelte-kit/
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Firebase cache directory
.firebase/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Vite logs files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# =============================================================================
# Java — https://github.com/github/gitignore/blob/main/Java.gitignore
# =============================================================================
# Compiled class file
*.class
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# =============================================================================
# Project-specific
# =============================================================================
# XML (Maven generates many; allow specific pom.xml files)
*.xml
!tests/test_languages/fixtures/**/pom.xml
!code_to_optimize/java/pom.xml
!codeflash-java-runtime/pom.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
.gradle/
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# Credentials
*.pem
**/.env
# CMake
cmake-build-*/
# IDEs
.idea/
.vscode/*
!.vscode/mcp.json
!.vscode/extensions.json
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# Nuitka
**/dist-nuitka/**
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# npm
**/.npmrc
# Mac
.DS_Store
WARP.MD
.mcp.json
.tessl/
tessl.json
# Playwright MCP
.playwright-mcp/
# Claude Code - track shared rules, ignore local config
# Tessl — .tessl/.gitignore handles tiles/RULES.md internally
.tessl/session-data/
# Claude Code — track shared config, ignore local state
.claude/*
!.claude/rules/
!.claude/settings.json
!.claude/hooks/
**/node_modules/**
**/dist-nuitka/**
**/.npmrc
!.claude/skills/
!.claude/settings.json
# Tessl auto-generates AGENTS.md on install; ignore to avoid cluttering git status
AGENTS.md
.serena/
# Test fixture lockfiles — prevents Dependabot from scanning them
code_to_optimize/**/package-lock.json
# Other tools
.codeflash/

View file

@ -1,8 +1,15 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.8
hooks:
# Run the linter.
- id: ruff-check
# Run the formatter.
- id: ruff-format
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.8
hooks:
- id: ruff-check
- id: ruff-format
- repo: local
hooks:
- id: mypy
name: mypy
entry: uv run mypy --non-interactive --config-file pyproject.toml
language: system
types: [python]
require_serial: true

24
.tessl/missing-tiles.txt Normal file
View file

@ -0,0 +1,24 @@
# PyPI
tessl/pypi-tree-sitter-javascript
tessl/pypi-tree-sitter-typescript
tessl/pypi-tree-sitter-java
tessl/pypi-tree-sitter-groovy
tessl/pypi-tree-sitter-kotlin
tessl/pypi-pytest-timeout
tessl/pypi-junitparser
tessl/pypi-isort
tessl/pypi-line-profiler
tessl/pypi-pytest-asyncio
tessl/pypi-pytest-memray
tessl/pypi-unidiff
tessl/pypi-ruff
# npm
tessl/npm-msgpack--msgpack
tessl/npm-babel--register
tessl/npm-babel--preset-env
# Maven
tessl/maven-com-esotericsoftware--kryo
tessl/maven-org-objenesis--objenesis
tessl/maven-org-xerial--sqlite-jdbc
tessl/maven-org-ow2-asm--asm
tessl/maven-org-ow2-asm--asm-commons

View file

@ -0,0 +1,405 @@
# Annotations
Gson provides several annotations to control serialization and deserialization behavior directly on fields and classes without requiring configuration changes.
## @SerializedName
Specifies alternative names for fields during serialization and deserialization.
```java { .api }
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SerializedName {
String value();
String[] alternate() default {};
}
```
**Basic usage:**
```java
public class Person {
@SerializedName("full_name")
private String name;
@SerializedName("years_old")
private int age;
}
// JSON: {"full_name":"Alice","years_old":30}
```
**Multiple alternative names for deserialization:**
```java
public class Person {
@SerializedName(value = "name", alternate = {"full_name", "firstName", "first_name"})
private String name;
}
// Can deserialize from any of these JSON formats:
// {"name":"Alice"}
// {"full_name":"Alice"}
// {"firstName":"Alice"}
// {"first_name":"Alice"}
```
## @Expose
Controls which fields are included in serialization and deserialization when using `excludeFieldsWithoutExposeAnnotation()`.
```java { .api }
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Expose {
boolean serialize() default true;
boolean deserialize() default true;
}
```
**Usage:**
```java
public class User {
@Expose
private String name;
@Expose(serialize = false) // Only for deserialization
private String password;
@Expose(deserialize = false) // Only for serialization
private String token;
private String internalId; // Not exposed
}
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
// Only fields marked with @Expose will be processed
```
## @Since
Marks fields as available since a specific API version.
```java { .api }
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Since {
double value();
}
```
**Usage:**
```java
public class ApiResponse {
private String status;
@Since(1.1)
private String message;
@Since(2.0)
private List<String> errors;
}
// Only include fields from version 1.1 and later
Gson gson = new GsonBuilder()
.setVersion(1.1)
.create();
// 'status' and 'message' will be included, but 'errors' will be excluded
```
## @Until
Marks fields as available until a specific API version.
```java { .api }
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Until {
double value();
}
```
**Usage:**
```java
public class LegacyResponse {
private String data;
@Until(2.0)
private String oldFormat; // Deprecated in version 2.0
@Since(2.0)
private String newFormat; // Added in version 2.0
}
// Version 1.5: includes 'data' and 'oldFormat'
Gson gson15 = new GsonBuilder().setVersion(1.5).create();
// Version 2.1: includes 'data' and 'newFormat'
Gson gson21 = new GsonBuilder().setVersion(2.1).create();
```
## @JsonAdapter
Specifies a custom TypeAdapter, JsonSerializer, JsonDeserializer, or TypeAdapterFactory for a field or class.
```java { .api }
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonAdapter {
Class<?> value();
boolean nullSafe() default true;
}
```
**Field-level adapter:**
```java
public class Event {
private String name;
@JsonAdapter(DateAdapter.class)
private Date timestamp;
}
public class DateAdapter extends TypeAdapter<Date> {
private final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void write(JsonWriter out, Date date) throws IOException {
if (date == null) {
out.nullValue();
} else {
out.value(format.format(date));
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
return format.parse(in.nextString());
} catch (ParseException e) {
throw new IOException(e);
}
}
}
```
**Class-level adapter:**
```java
@JsonAdapter(ColorAdapter.class)
public class Color {
private int red, green, blue;
public Color(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
}
public class ColorAdapter extends TypeAdapter<Color> {
@Override
public void write(JsonWriter out, Color color) throws IOException {
if (color == null) {
out.nullValue();
} else {
String hex = String.format("#%02x%02x%02x", color.red, color.green, color.blue);
out.value(hex);
}
}
@Override
public Color read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String hex = in.nextString();
if (hex.startsWith("#")) {
hex = hex.substring(1);
}
int rgb = Integer.parseInt(hex, 16);
return new Color((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
}
}
// Usage: Color serializes as "#ff0000" instead of {"red":255,"green":0,"blue":0}
```
**Using with JsonSerializer/JsonDeserializer:**
```java
public class User {
private String name;
@JsonAdapter(PasswordSerializer.class)
private String password;
}
public class PasswordSerializer implements JsonSerializer<String>, JsonDeserializer<String> {
@Override
public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive("***"); // Always serialize as stars
}
@Override
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
return json.getAsString(); // Deserialize normally
}
}
```
**Using with TypeAdapterFactory:**
```java
@JsonAdapter(CaseInsensitiveEnumAdapterFactory.class)
public enum Status {
ACTIVE, INACTIVE, PENDING
}
public class CaseInsensitiveEnumAdapterFactory implements TypeAdapterFactory {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (!rawType.isEnum()) {
return null;
}
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.toString());
}
}
@Override
@SuppressWarnings("unchecked")
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String value = in.nextString().toUpperCase();
return (T) Enum.valueOf((Class<Enum>) rawType, value);
}
};
}
}
```
## Combining Annotations
Annotations can be combined for comprehensive control:
```java
public class Product {
@SerializedName("product_id")
@Since(1.0)
private String id;
@SerializedName("product_name")
@Expose
private String name;
@SerializedName("price_cents")
@JsonAdapter(MoneyAdapter.class)
private Money price;
@Until(2.0)
private String oldCategory;
@Since(2.0)
@SerializedName("category_info")
private Category category;
@Expose(serialize = false)
private String internalNotes;
}
```
## Annotation Processing Order
Gson processes annotations in the following priority:
1. **@JsonAdapter** - Takes highest precedence
2. **@SerializedName** - Controls field naming
3. **@Expose** - Controls field inclusion (when enabled)
4. **@Since/@Until** - Controls version-based inclusion
5. **Field modifiers** - Processed based on GsonBuilder configuration
**Example:**
```java
public class Example {
@JsonAdapter(CustomAdapter.class) // 1. Custom adapter used
@SerializedName("custom_field") // 2. Field name in JSON
@Expose(serialize = false) // 3. Only deserialize (if expose filtering enabled)
@Since(1.1) // 4. Only if version >= 1.1
private String field;
}
```
## Best Practices
**Use @SerializedName for API compatibility:**
```java
public class ApiModel {
@SerializedName("user_id")
private String userId; // Java convention: camelCase
@SerializedName("created_at")
private Date createdAt; // API uses snake_case
}
```
**Use @Expose for security:**
```java
public class User {
@Expose
private String username;
@Expose
private String email;
private String passwordHash; // Never expose sensitive data
@Expose(serialize = false)
private String password; // Accept for input, never output
}
```
**Use versioning for API evolution:**
```java
public class ApiResponse {
private String status;
@Until(1.9)
private String oldErrorMessage;
@Since(2.0)
private ErrorDetails errorDetails;
}
```
**Use @JsonAdapter for domain-specific serialization:**
```java
public class Order {
@JsonAdapter(CurrencyAdapter.class)
private BigDecimal totalAmount;
@JsonAdapter(TimestampAdapter.class)
private Instant orderTime;
@JsonAdapter(Base64Adapter.class)
private byte[] signature;
}
```

View file

@ -0,0 +1,357 @@
# Configuration
Gson provides extensive configuration options through the GsonBuilder class for customizing serialization and deserialization behavior.
## GsonBuilder
Builder pattern class for configuring Gson instances.
```java { .api }
public final class GsonBuilder {
public GsonBuilder();
// JSON formatting
public GsonBuilder setPrettyPrinting();
public GsonBuilder setFormattingStyle(FormattingStyle formattingStyle);
public GsonBuilder disableHtmlEscaping();
// Null handling
public GsonBuilder serializeNulls();
// Field naming
public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention);
public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy);
// Version control
public GsonBuilder setVersion(double version);
// Field exposure
public GsonBuilder excludeFieldsWithoutExposeAnnotation();
public GsonBuilder excludeFieldsWithModifiers(int... modifiers);
// Exclusion strategies
public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies);
public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy);
public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy);
// Type adapters
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter);
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory);
public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter);
// Number handling
public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy);
public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy);
public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStrategy);
public GsonBuilder serializeSpecialFloatingPointValues();
// Date formatting
public GsonBuilder setDateFormat(String pattern);
public GsonBuilder setDateFormat(int dateStyle);
public GsonBuilder setDateFormat(int dateStyle, int timeStyle);
// JSON parsing strictness
public GsonBuilder setLenient();
public GsonBuilder setStrictness(Strictness strictness);
// Advanced options
public GsonBuilder enableComplexMapKeySerialization();
public GsonBuilder disableInnerClassSerialization();
public GsonBuilder generateNonExecutableJson();
public GsonBuilder disableJdkUnsafe();
public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter);
// Build final instance
public Gson create();
}
```
## Basic Configuration
**Creating a configured Gson instance:**
```java
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.create();
```
## Field Naming Policies
Control how Java field names are converted to JSON property names.
```java { .api }
public enum FieldNamingPolicy implements FieldNamingStrategy {
IDENTITY,
UPPER_CAMEL_CASE,
UPPER_CAMEL_CASE_WITH_SPACES,
UPPER_CASE_WITH_UNDERSCORES,
LOWER_CASE_WITH_UNDERSCORES,
LOWER_CASE_WITH_DASHES,
LOWER_CASE_WITH_DOTS
}
```
**Usage:**
```java
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
class Person {
String firstName; // becomes "first_name" in JSON
String lastName; // becomes "last_name" in JSON
}
```
## Custom Field Naming Strategy
```java { .api }
public interface FieldNamingStrategy {
public String translateName(Field f);
}
```
**Usage:**
```java
FieldNamingStrategy customStrategy = field -> {
return "prefix_" + field.getName().toLowerCase();
};
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(customStrategy)
.create();
```
## Exclusion Strategies
Control which fields and classes are included in serialization/deserialization.
```java { .api }
public interface ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes f);
public boolean shouldSkipClass(Class<?> clazz);
}
```
**Usage:**
```java
ExclusionStrategy strategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getName().startsWith("internal");
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return clazz.getName().contains("Internal");
}
};
Gson gson = new GsonBuilder()
.setExclusionStrategies(strategy)
.create();
```
## Field Attributes
Information about fields for exclusion strategies.
```java { .api }
public final class FieldAttributes {
public Class<?> getDeclaringClass();
public String getName();
public Type getDeclaredType();
public Class<?> getDeclaredClass();
public <T extends Annotation> T getAnnotation(Class<T> annotation);
public Collection<Annotation> getAnnotations();
public boolean hasModifier(int modifier);
}
```
## Number Handling
### Long Serialization Policy
```java { .api }
public enum LongSerializationPolicy {
DEFAULT, // Serialize as numbers
STRING // Serialize as strings
}
```
**Usage:**
```java
Gson gson = new GsonBuilder()
.setLongSerializationPolicy(LongSerializationPolicy.STRING)
.create();
```
### Number Strategies
```java { .api }
public interface ToNumberStrategy {
public Number readNumber(JsonReader in) throws IOException;
}
public enum ToNumberPolicy implements ToNumberStrategy {
DOUBLE,
LAZILY_PARSED_NUMBER,
LONG_OR_DOUBLE,
BIG_DECIMAL
}
```
**Usage:**
```java
Gson gson = new GsonBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL)
.setNumberToNumberStrategy(ToNumberPolicy.BIG_DECIMAL)
.create();
```
## Date Formatting
**Pattern-based formatting:**
```java
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
```
**Style-based formatting:**
```java
import java.text.DateFormat;
Gson gson = new GsonBuilder()
.setDateFormat(DateFormat.LONG)
.create();
// Or with both date and time styles
Gson gson = new GsonBuilder()
.setDateFormat(DateFormat.MEDIUM, DateFormat.SHORT)
.create();
```
## JSON Formatting
### Pretty Printing
```java
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
```
### Custom Formatting Style
```java { .api }
public class FormattingStyle {
public static FormattingStyle COMPACT;
public static FormattingStyle PRETTY;
public FormattingStyle withNewline(String newline);
public FormattingStyle withIndent(String indent);
public FormattingStyle withSpaceAfterSeparators(boolean spaceAfterSeparators);
}
```
**Usage:**
```java
FormattingStyle style = FormattingStyle.PRETTY
.withIndent(" ") // 4 spaces
.withNewline("\n");
Gson gson = new GsonBuilder()
.setFormattingStyle(style)
.create();
```
## Strictness Control
```java { .api }
public enum Strictness {
LENIENT, // Allows malformed JSON
STRICT // Requires well-formed JSON
}
```
**Usage:**
```java
// Lenient parsing (allows single quotes, unquoted names, etc.)
Gson gson = new GsonBuilder()
.setLenient()
.create();
// Or explicitly set strictness
Gson gson = new GsonBuilder()
.setStrictness(Strictness.LENIENT)
.create();
```
## Advanced Configuration
### Complex Map Keys
Enable serialization of complex objects as map keys:
```java
Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
```
### HTML Escaping
Disable HTML character escaping:
```java
Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.create();
```
### Reflection Access Control
```java { .api }
public interface ReflectionAccessFilter {
enum FilterResult { ALLOW, BLOCK_INACCESSIBLE, BLOCK_ALL }
FilterResult check(Class<?> rawClass);
}
```
**Usage:**
```java
ReflectionAccessFilter filter = new ReflectionAccessFilter() {
@Override
public FilterResult check(Class<?> rawClass) {
if (rawClass.getPackage().getName().startsWith("com.example.internal")) {
return FilterResult.BLOCK_ALL;
}
return FilterResult.ALLOW;
}
};
Gson gson = new GsonBuilder()
.addReflectionAccessFilter(filter)
.create();
```
### Version Control
Use `@Since` and `@Until` annotations with version setting:
```java
Gson gson = new GsonBuilder()
.setVersion(2.0)
.create();
```
### Modifier-based Exclusion
Exclude fields based on Java modifiers:
```java
import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
.create();
```

View file

@ -0,0 +1,211 @@
# Gson
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source-code access to.
## Package Information
- **Package Name**: com.google.code.gson:gson
- **Package Type**: Maven
- **Language**: Java
- **Installation**:
```xml
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.1</version>
</dependency>
```
## Core Imports
```java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
```
For JSON tree model:
```java
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonPrimitive;
```
For streaming:
```java
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
```
## Basic Usage
```java
import com.google.gson.Gson;
// Simple object serialization
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// Create Gson instance
Gson gson = new Gson();
// Convert Java object to JSON
Person person = new Person("John", 30);
String json = gson.toJson(person);
// Result: {"name":"John","age":30}
// Convert JSON to Java object
String jsonString = "{\"name\":\"Jane\",\"age\":25}";
Person deserializedPerson = gson.fromJson(jsonString, Person.class);
```
## Architecture
Gson is built around several key components:
- **Gson Class**: Main entry point providing `toJson()` and `fromJson()` methods
- **GsonBuilder**: Builder pattern for configuring Gson instances with custom settings
- **JSON Tree Model**: Object representation (JsonElement hierarchy) for manipulating JSON as trees
- **Streaming API**: Memory-efficient reading/writing for large JSON documents
- **Type Adapters**: Pluggable serialization/deserialization for custom types
- **Reflection System**: Automatic object mapping using Java reflection
## Capabilities
### Object Serialization and Deserialization
Core functionality for converting between Java objects and JSON strings. Handles arbitrarily complex objects with deep inheritance hierarchies and generic types.
```java { .api }
public String toJson(Object src);
public String toJson(Object src, Type typeOfSrc);
public <T> T fromJson(String json, Class<T> classOfT);
public <T> T fromJson(String json, Type typeOfT);
public <T> T fromJson(String json, TypeToken<T> typeOfT);
```
[Object Serialization](./object-serialization.md)
### JSON Tree Model
Tree-based API for building and manipulating JSON structures programmatically. Useful when you need to modify JSON before serialization or after deserialization.
```java { .api }
public JsonElement toJsonTree(Object src);
public <T> T fromJson(JsonElement json, Class<T> classOfT);
abstract class JsonElement {
public boolean isJsonObject();
public boolean isJsonArray();
public boolean isJsonPrimitive();
public boolean isJsonNull();
}
```
[JSON Tree Model](./json-tree-model.md)
### Configuration and Customization
Extensive configuration options for controlling serialization behavior, field naming, date formatting, and custom type adapters.
```java { .api }
public final class GsonBuilder {
public GsonBuilder setPrettyPrinting();
public GsonBuilder serializeNulls();
public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention);
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter);
public Gson create();
}
```
[Configuration](./configuration.md)
### Streaming API
Memory-efficient streaming API for processing large JSON documents without loading them entirely into memory.
```java { .api }
public class JsonReader implements Closeable {
public JsonToken peek() throws IOException;
public String nextString() throws IOException;
public int nextInt() throws IOException;
public void beginObject() throws IOException;
public void endObject() throws IOException;
}
public class JsonWriter implements Closeable, Flushable {
public JsonWriter name(String name) throws IOException;
public JsonWriter value(String value) throws IOException;
public JsonWriter beginObject() throws IOException;
public JsonWriter endObject() throws IOException;
}
```
[Streaming API](./streaming-api.md)
### Type Adapters
Pluggable system for custom serialization and deserialization logic. Allows fine-grained control over how specific types are converted.
```java { .api }
public abstract class TypeAdapter<T> {
public abstract void write(JsonWriter out, T value) throws IOException;
public abstract T read(JsonReader in) throws IOException;
}
public interface TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
```
[Type Adapters](./type-adapters.md)
### Annotations
Annotations for controlling serialization behavior on fields and classes without requiring configuration changes.
```java { .api }
@SerializedName("custom_name")
@Expose(serialize = true, deserialize = true)
@Since(1.0)
@Until(2.0)
@JsonAdapter(CustomAdapter.class)
```
[Annotations](./annotations.md)
## Types
```java { .api }
// Core exception types
class JsonParseException extends RuntimeException {}
class JsonSyntaxException extends JsonParseException {}
class JsonIOException extends JsonParseException {}
// Enums for configuration
enum FieldNamingPolicy {
IDENTITY, UPPER_CAMEL_CASE, UPPER_CAMEL_CASE_WITH_SPACES,
UPPER_CASE_WITH_UNDERSCORES, LOWER_CASE_WITH_UNDERSCORES,
LOWER_CASE_WITH_DASHES, LOWER_CASE_WITH_DOTS
}
enum LongSerializationPolicy { DEFAULT, STRING }
enum ToNumberPolicy implements ToNumberStrategy {
DOUBLE, LAZILY_PARSED_NUMBER, LONG_OR_DOUBLE, BIG_DECIMAL
}
// Type handling
class TypeToken<T> {
public static <T> TypeToken<T> get(Class<T> type);
public static TypeToken<?> get(Type type);
public Type getType();
}
```

View file

@ -0,0 +1,317 @@
# JSON Tree Model
Gson provides a tree-based API for building and manipulating JSON structures programmatically. This is useful when you need to modify JSON before serialization or after deserialization.
## JsonElement Hierarchy
```java { .api }
public abstract class JsonElement {
public abstract JsonElement deepCopy();
// Type checking methods
public boolean isJsonArray();
public boolean isJsonObject();
public boolean isJsonPrimitive();
public boolean isJsonNull();
// Conversion methods
public JsonObject getAsJsonObject();
public JsonArray getAsJsonArray();
public JsonPrimitive getAsJsonPrimitive();
public JsonNull getAsJsonNull();
// Primitive value extraction
public boolean getAsBoolean();
public Number getAsNumber();
public String getAsString();
public double getAsDouble();
public float getAsFloat();
public long getAsLong();
public int getAsInt();
public byte getAsByte();
public char getAsCharacter();
}
```
## JsonObject
Represents JSON objects with key-value pairs.
```java { .api }
public final class JsonObject extends JsonElement {
public JsonObject();
public JsonObject deepCopy();
// Adding elements
public void add(String property, JsonElement value);
public void addProperty(String property, String value);
public void addProperty(String property, Number value);
public void addProperty(String property, Boolean value);
public void addProperty(String property, Character value);
// Accessing elements
public JsonElement get(String memberName);
public JsonPrimitive getAsJsonPrimitive(String memberName);
public JsonArray getAsJsonArray(String memberName);
public JsonObject getAsJsonObject(String memberName);
// Management
public JsonElement remove(String property);
public boolean has(String memberName);
public Set<Map.Entry<String, JsonElement>> entrySet();
public Set<String> keySet();
public int size();
public boolean isEmpty();
}
```
**Usage:**
```java
// Create JSON object programmatically
JsonObject person = new JsonObject();
person.addProperty("name", "Alice");
person.addProperty("age", 30);
person.addProperty("active", true);
// Convert to string
Gson gson = new Gson();
String json = gson.toJson(person);
// Result: {"name":"Alice","age":30,"active":true}
// Access properties
String name = person.get("name").getAsString();
int age = person.get("age").getAsInt();
boolean active = person.get("active").getAsBoolean();
// Check if property exists
if (person.has("email")) {
String email = person.get("email").getAsString();
}
```
## JsonArray
Represents JSON arrays.
```java { .api }
public final class JsonArray extends JsonElement {
public JsonArray();
public JsonArray(int capacity);
public JsonArray deepCopy();
// Adding elements
public void add(JsonElement element);
public void add(Boolean bool);
public void add(Character character);
public void add(Number number);
public void add(String string);
// Accessing elements
public JsonElement get(int index);
// Management
public JsonElement remove(int index);
public boolean remove(JsonElement element);
public boolean contains(JsonElement element);
public int size();
public boolean isEmpty();
// Iteration
public Iterator<JsonElement> iterator();
}
```
**Usage:**
```java
// Create JSON array
JsonArray numbers = new JsonArray();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// Convert to string
String json = gson.toJson(numbers);
// Result: [1,2,3]
// Access elements
for (int i = 0; i < numbers.size(); i++) {
int value = numbers.get(i).getAsInt();
System.out.println(value);
}
// Using iterator
for (JsonElement element : numbers) {
int value = element.getAsInt();
System.out.println(value);
}
```
## JsonPrimitive
Represents JSON primitive values (strings, numbers, booleans).
```java { .api }
public final class JsonPrimitive extends JsonElement {
public JsonPrimitive(Boolean bool);
public JsonPrimitive(Number number);
public JsonPrimitive(String string);
public JsonPrimitive(Character c);
public JsonPrimitive deepCopy();
public boolean isBoolean();
public boolean isNumber();
public boolean isString();
}
```
**Usage:**
```java
JsonPrimitive stringValue = new JsonPrimitive("Hello");
JsonPrimitive numberValue = new JsonPrimitive(42);
JsonPrimitive boolValue = new JsonPrimitive(true);
// Check types
if (stringValue.isString()) {
String str = stringValue.getAsString();
}
if (numberValue.isNumber()) {
int num = numberValue.getAsInt();
double dbl = numberValue.getAsDouble();
}
```
## JsonNull
Represents JSON null values.
```java { .api }
public final class JsonNull extends JsonElement {
public static final JsonNull INSTANCE;
public JsonNull deepCopy();
}
```
**Usage:**
```java
JsonObject obj = new JsonObject();
obj.add("nullValue", JsonNull.INSTANCE);
```
## Tree Conversion
Convert between objects and JSON trees:
```java { .api }
// Object to tree
public JsonElement toJsonTree(Object src);
public JsonElement toJsonTree(Object src, Type typeOfSrc);
// Tree to object
public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException;
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException;
public <T> T fromJson(JsonElement json, TypeToken<T> typeOfT) throws JsonSyntaxException;
```
**Usage:**
```java
// Object to tree
Person person = new Person("Bob", 25);
JsonElement tree = gson.toJsonTree(person);
// Modify the tree
JsonObject obj = tree.getAsJsonObject();
obj.addProperty("email", "bob@example.com");
// Tree back to object
Person modifiedPerson = gson.fromJson(obj, Person.class);
// Tree to JSON string
String json = gson.toJson(tree);
```
## JsonParser
Utility for parsing JSON strings into JsonElement trees.
```java { .api }
public final class JsonParser {
public static JsonElement parseString(String json) throws JsonSyntaxException;
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException;
public static JsonElement parseReader(JsonReader reader) throws JsonIOException, JsonSyntaxException;
}
```
**Usage:**
```java
String json = "{\"name\":\"Alice\",\"age\":30}";
JsonElement element = JsonParser.parseString(json);
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
String name = obj.get("name").getAsString();
}
```
## JsonStreamParser
Streaming parser for processing multiple JSON values from a single reader.
```java { .api }
public final class JsonStreamParser implements Iterator<JsonElement> {
public JsonStreamParser(Reader reader);
public JsonStreamParser(String json);
public boolean hasNext();
public JsonElement next() throws JsonParseException;
}
```
**Usage:**
```java
String multipleJson = "{\"name\":\"Alice\"} {\"name\":\"Bob\"} [1,2,3]";
JsonStreamParser parser = new JsonStreamParser(multipleJson);
while (parser.hasNext()) {
JsonElement element = parser.next();
System.out.println(element);
}
```
## Complex Tree Manipulation
**Building nested structures:**
```java
JsonObject address = new JsonObject();
address.addProperty("street", "123 Main St");
address.addProperty("city", "Springfield");
JsonObject person = new JsonObject();
person.addProperty("name", "John");
person.add("address", address);
JsonArray hobbies = new JsonArray();
hobbies.add("reading");
hobbies.add("swimming");
person.add("hobbies", hobbies);
```
**Traversing complex structures:**
```java
JsonObject root = JsonParser.parseString(complexJson).getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : root.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (value.isJsonObject()) {
JsonObject nested = value.getAsJsonObject();
// Process nested object
} else if (value.isJsonArray()) {
JsonArray array = value.getAsJsonArray();
for (JsonElement item : array) {
// Process array item
}
}
}
```

View file

@ -0,0 +1,217 @@
# Object Serialization
The core functionality of Gson for converting between Java objects and JSON strings.
## Serialization (Java to JSON)
### Basic Serialization
```java { .api }
public String toJson(Object src);
```
Converts any Java object to its JSON string representation using default settings.
**Usage:**
```java
Gson gson = new Gson();
Person person = new Person("Alice", 30);
String json = gson.toJson(person);
// Result: {"name":"Alice","age":30}
```
### Serialization with Type Information
```java { .api }
public String toJson(Object src, Type typeOfSrc);
```
Explicitly specifies the type for serialization, useful for generic collections and inheritance hierarchies.
**Usage:**
```java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Type listType = new TypeToken<List<String>>(){}.getType();
String json = gson.toJson(names, listType);
// Result: ["Alice","Bob","Charlie"]
```
### Serialization to Writer
```java { .api }
public void toJson(Object src, Appendable writer) throws JsonIOException;
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException;
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException;
```
Serialize directly to a Writer or JsonWriter for memory efficiency or streaming output.
**Usage:**
```java
StringWriter writer = new StringWriter();
gson.toJson(person, writer);
String json = writer.toString();
```
## Deserialization (JSON to Java)
### Basic Deserialization
```java { .api }
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException;
```
Converts JSON string to Java object of the specified class.
**Usage:**
```java
String json = "{\"name\":\"Bob\",\"age\":25}";
Person person = gson.fromJson(json, Person.class);
```
### Deserialization with Generic Types
```java { .api }
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException;
public <T> T fromJson(String json, TypeToken<T> typeOfT) throws JsonSyntaxException;
```
Handles generic types like collections and maps using Type or TypeToken.
**Usage:**
```java
String json = "[\"Alice\",\"Bob\",\"Charlie\"]";
Type listType = new TypeToken<List<String>>(){}.getType();
List<String> names = gson.fromJson(json, listType);
// Or using TypeToken directly
TypeToken<List<String>> token = new TypeToken<List<String>>(){};
List<String> names = gson.fromJson(json, token);
```
### Deserialization from Reader
```java { .api }
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonIOException, JsonSyntaxException;
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException;
public <T> T fromJson(Reader json, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException;
```
Deserialize from a Reader for memory-efficient processing of large files.
**Usage:**
```java
FileReader reader = new FileReader("data.json");
Person person = gson.fromJson(reader, Person.class);
reader.close();
```
### Deserialization from JsonReader
```java { .api }
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException;
public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException;
```
Deserialize from JsonReader for streaming API integration.
**Usage:**
```java
JsonReader reader = new JsonReader(new StringReader(json));
Person person = gson.fromJson(reader, Person.class);
reader.close();
```
## Complex Object Handling
### Collections
Gson automatically handles standard Java collections:
```java
// Lists
List<String> list = Arrays.asList("a", "b", "c");
String json = gson.toJson(list);
List<String> restored = gson.fromJson(json, new TypeToken<List<String>>(){}.getType());
// Maps
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
String json = gson.toJson(map);
Map<String, Integer> restored = gson.fromJson(json, new TypeToken<Map<String, Integer>>(){}.getType());
```
### Arrays
```java
int[] numbers = {1, 2, 3, 4, 5};
String json = gson.toJson(numbers);
int[] restored = gson.fromJson(json, int[].class);
```
### Nested Objects
```java
class Address {
String street;
String city;
}
class Person {
String name;
Address address;
}
Person person = new Person();
person.name = "John";
person.address = new Address();
person.address.street = "123 Main St";
person.address.city = "Springfield";
String json = gson.toJson(person);
Person restored = gson.fromJson(json, Person.class);
```
### Inheritance
```java
class Animal {
String name;
}
class Dog extends Animal {
String breed;
}
Dog dog = new Dog();
dog.name = "Buddy";
dog.breed = "Golden Retriever";
// Serialize as Dog type
String json = gson.toJson(dog, Dog.class);
Dog restored = gson.fromJson(json, Dog.class);
```
## Error Handling
```java { .api }
class JsonSyntaxException extends JsonParseException {
// Thrown when JSON syntax is invalid
}
class JsonIOException extends JsonParseException {
// Thrown when I/O errors occur during reading/writing
}
```
**Usage:**
```java
try {
Person person = gson.fromJson(invalidJson, Person.class);
} catch (JsonSyntaxException e) {
System.err.println("Invalid JSON syntax: " + e.getMessage());
} catch (JsonIOException e) {
System.err.println("I/O error: " + e.getMessage());
}
```

View file

@ -0,0 +1,396 @@
# Streaming API
Gson's streaming API provides memory-efficient processing of JSON data through JsonReader and JsonWriter classes. This is essential for handling large JSON documents without loading them entirely into memory.
## JsonReader
Read JSON content in a streaming fashion.
```java { .api }
public class JsonReader implements Closeable {
public JsonReader(Reader in);
// Configuration
public void setLenient(boolean lenient);
public boolean isLenient();
public void setStrictness(Strictness strictness);
public Strictness getStrictness();
// Navigation
public JsonToken peek() throws IOException;
public JsonToken nextToken() throws IOException;
public void skipValue() throws IOException;
// Object navigation
public void beginObject() throws IOException;
public void endObject() throws IOException;
public String nextName() throws IOException;
// Array navigation
public void beginArray() throws IOException;
public void endArray() throws IOException;
// Value reading
public String nextString() throws IOException;
public boolean nextBoolean() throws IOException;
public void nextNull() throws IOException;
public double nextDouble() throws IOException;
public long nextLong() throws IOException;
public int nextInt() throws IOException;
// State
public boolean hasNext() throws IOException;
public String getPath();
// Resource management
public void close() throws IOException;
}
```
### JsonToken Enum
```java { .api }
public enum JsonToken {
BEGIN_ARRAY,
END_ARRAY,
BEGIN_OBJECT,
END_OBJECT,
NAME,
STRING,
NUMBER,
BOOLEAN,
NULL,
END_DOCUMENT
}
```
**Reading JSON objects:**
```java
String json = "{\"name\":\"Alice\",\"age\":30,\"active\":true}";
JsonReader reader = new JsonReader(new StringReader(json));
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
switch (name) {
case "name":
String personName = reader.nextString();
break;
case "age":
int age = reader.nextInt();
break;
case "active":
boolean active = reader.nextBoolean();
break;
default:
reader.skipValue(); // Skip unknown properties
break;
}
}
reader.endObject();
reader.close();
```
**Reading JSON arrays:**
```java
String json = "[1,2,3,4,5]";
JsonReader reader = new JsonReader(new StringReader(json));
reader.beginArray();
while (reader.hasNext()) {
int value = reader.nextInt();
System.out.println(value);
}
reader.endArray();
reader.close();
```
**Reading complex nested structures:**
```java
String json = "{\"users\":[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]}";
JsonReader reader = new JsonReader(new StringReader(json));
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if ("users".equals(name)) {
reader.beginArray();
while (reader.hasNext()) {
reader.beginObject();
String userName = null;
int age = 0;
while (reader.hasNext()) {
String fieldName = reader.nextName();
if ("name".equals(fieldName)) {
userName = reader.nextString();
} else if ("age".equals(fieldName)) {
age = reader.nextInt();
} else {
reader.skipValue();
}
}
reader.endObject();
System.out.println("User: " + userName + ", Age: " + age);
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
reader.close();
```
## JsonWriter
Write JSON content in a streaming fashion.
```java { .api }
public class JsonWriter implements Closeable, Flushable {
public JsonWriter(Writer out);
// Configuration
public void setLenient(boolean lenient);
public boolean isLenient();
public void setStrictness(Strictness strictness);
public Strictness getStrictness();
public void setIndent(String indent);
public void setHtmlSafe(boolean htmlSafe);
public boolean isHtmlSafe();
public void setSerializeNulls(boolean serializeNulls);
public boolean getSerializeNulls();
// Object writing
public JsonWriter beginObject() throws IOException;
public JsonWriter endObject() throws IOException;
public JsonWriter name(String name) throws IOException;
// Array writing
public JsonWriter beginArray() throws IOException;
public JsonWriter endArray() throws IOException;
// Value writing
public JsonWriter value(String value) throws IOException;
public JsonWriter nullValue() throws IOException;
public JsonWriter value(boolean value) throws IOException;
public JsonWriter value(Boolean value) throws IOException;
public JsonWriter value(double value) throws IOException;
public JsonWriter value(long value) throws IOException;
public JsonWriter value(Number value) throws IOException;
// JSON value writing
public JsonWriter jsonValue(String value) throws IOException;
// Resource management
public void flush() throws IOException;
public void close() throws IOException;
}
```
**Writing JSON objects:**
```java
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginObject();
writer.name("name").value("Alice");
writer.name("age").value(30);
writer.name("active").value(true);
writer.name("salary").nullValue();
writer.endObject();
writer.close();
String json = stringWriter.toString();
// Result: {"name":"Alice","age":30,"active":true,"salary":null}
```
**Writing JSON arrays:**
```java
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginArray();
writer.value(1);
writer.value(2);
writer.value(3);
writer.endArray();
writer.close();
String json = stringWriter.toString();
// Result: [1,2,3]
```
**Writing complex nested structures:**
```java
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginObject();
writer.name("users");
writer.beginArray();
writer.beginObject();
writer.name("name").value("Alice");
writer.name("age").value(30);
writer.name("hobbies");
writer.beginArray();
writer.value("reading");
writer.value("swimming");
writer.endArray();
writer.endObject();
writer.beginObject();
writer.name("name").value("Bob");
writer.name("age").value(25);
writer.name("hobbies");
writer.beginArray();
writer.value("gaming");
writer.endArray();
writer.endObject();
writer.endArray();
writer.endObject();
writer.close();
```
## Gson Integration
Create JsonReader and JsonWriter through Gson for consistent configuration:
```java { .api }
public JsonWriter newJsonWriter(Writer writer) throws IOException;
public JsonReader newJsonReader(Reader reader);
```
**Usage:**
```java
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.setLenient()
.create();
// Create configured writer
StringWriter stringWriter = new StringWriter();
JsonWriter writer = gson.newJsonWriter(stringWriter);
// Create configured reader
JsonReader reader = gson.newJsonReader(new StringReader(json));
```
## Error Handling
```java { .api }
class MalformedJsonException extends IOException {
// Thrown when JSON structure is invalid
}
```
**Error handling example:**
```java
try {
JsonReader reader = new JsonReader(new StringReader(malformedJson));
reader.beginObject();
// ... reading logic
} catch (MalformedJsonException e) {
System.err.println("Malformed JSON: " + e.getMessage());
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
}
```
## Advanced Usage
### Lenient Mode
Allow relaxed JSON syntax:
```java
JsonReader reader = new JsonReader(new StringReader(json));
reader.setLenient(true);
// Now can read: {name: 'Alice', age: 30} (unquoted names, single quotes)
```
### Pretty Printing
Configure indentation for readable output:
```java
JsonWriter writer = new JsonWriter(new FileWriter("output.json"));
writer.setIndent(" "); // 2-space indentation
writer.beginObject();
writer.name("name").value("Alice");
writer.name("details");
writer.beginObject();
writer.name("age").value(30);
writer.endObject();
writer.endObject();
```
### Large File Processing
Process large JSON files efficiently:
```java
// Reading large file
try (FileReader fileReader = new FileReader("large-data.json");
JsonReader reader = new JsonReader(fileReader)) {
reader.beginArray();
while (reader.hasNext()) {
// Process each object without loading entire file
processObject(reader);
}
reader.endArray();
}
private void processObject(JsonReader reader) throws IOException {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
// Handle each field as needed
if ("data".equals(name)) {
// Process data field
} else {
reader.skipValue();
}
}
reader.endObject();
}
```
### Custom Adapter Integration
Use streaming API within custom TypeAdapters:
```java
public class PersonAdapter extends TypeAdapter<Person> {
@Override
public void write(JsonWriter out, Person person) throws IOException {
out.beginObject();
out.name("name").value(person.getName());
out.name("age").value(person.getAge());
out.endObject();
}
@Override
public Person read(JsonReader in) throws IOException {
Person person = new Person();
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "name":
person.setName(in.nextString());
break;
case "age":
person.setAge(in.nextInt());
break;
default:
in.skipValue();
break;
}
}
in.endObject();
return person;
}
}
```

View file

@ -0,0 +1,393 @@
# Type Adapters
Type adapters provide fine-grained control over JSON serialization and deserialization for specific types. They are the most flexible way to customize Gson's behavior.
## TypeAdapter Abstract Class
The base class for all type adapters.
```java { .api }
public abstract class TypeAdapter<T> {
public abstract void write(JsonWriter out, T value) throws IOException;
public abstract T read(JsonReader in) throws IOException;
// Convenience methods
public final String toJson(T value);
public final void toJson(Writer out, T value) throws IOException;
public final T fromJson(String json) throws IOException;
public final T fromJson(Reader in) throws IOException;
// Null handling
public final TypeAdapter<T> nullSafe();
}
```
## Creating Custom Type Adapters
**Basic type adapter example:**
```java
public class PersonAdapter extends TypeAdapter<Person> {
@Override
public void write(JsonWriter out, Person person) throws IOException {
if (person == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("name").value(person.getName());
out.name("age").value(person.getAge());
out.name("email").value(person.getEmail());
out.endObject();
}
@Override
public Person read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Person person = new Person();
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "name":
person.setName(in.nextString());
break;
case "age":
person.setAge(in.nextInt());
break;
case "email":
person.setEmail(in.nextString());
break;
default:
in.skipValue(); // Skip unknown properties
break;
}
}
in.endObject();
return person;
}
}
```
**Registering the adapter:**
```java
Gson gson = new GsonBuilder()
.registerTypeAdapter(Person.class, new PersonAdapter())
.create();
Person person = new Person("Alice", 30, "alice@example.com");
String json = gson.toJson(person);
Person restored = gson.fromJson(json, Person.class);
```
## TypeAdapterFactory Interface
Factory pattern for creating type adapters dynamically.
```java { .api }
public interface TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
```
**Example factory for enum handling:**
```java
public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (!rawType.isEnum()) {
return null; // This factory only handles enums
}
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.toString().toLowerCase());
}
}
@Override
@SuppressWarnings("unchecked")
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String value = in.nextString().toUpperCase();
return (T) Enum.valueOf((Class<Enum>) rawType, value);
}
};
}
}
```
**Registering the factory:**
```java
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory())
.create();
```
## Built-in Type Adapters
Gson provides many built-in type adapters accessible through the TypeAdapters utility class.
```java { .api }
public final class TypeAdapters {
public static final TypeAdapter<Class> CLASS;
public static final TypeAdapter<Boolean> BOOLEAN;
public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING;
public static final TypeAdapter<Number> BYTE;
public static final TypeAdapter<Number> SHORT;
public static final TypeAdapter<Number> INTEGER;
public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER;
public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN;
public static final TypeAdapter<Number> LONG;
public static final TypeAdapter<Number> FLOAT;
public static final TypeAdapter<Number> DOUBLE;
public static final TypeAdapter<Character> CHARACTER;
public static final TypeAdapter<String> STRING;
public static final TypeAdapter<StringBuilder> STRING_BUILDER;
public static final TypeAdapter<StringBuffer> STRING_BUFFER;
public static final TypeAdapter<URL> URL;
public static final TypeAdapter<URI> URI;
public static final TypeAdapter<UUID> UUID;
public static final TypeAdapter<Currency> CURRENCY;
public static final TypeAdapter<Calendar> CALENDAR;
public static final TypeAdapter<Locale> LOCALE;
public static final TypeAdapter<JsonElement> JSON_ELEMENT;
// Factory methods
public static <TT> TypeAdapterFactory newFactory(Class<TT> type, TypeAdapter<TT> typeAdapter);
public static <TT> TypeAdapterFactory newFactory(TypeToken<TT> type, TypeAdapter<TT> typeAdapter);
public static TypeAdapterFactory newFactoryForMultipleTypes(Class<?> base, Class<?> sub, TypeAdapter<?> typeAdapter);
}
```
## Advanced Type Adapter Patterns
### Generic Type Handling
```java
public class ListTypeAdapterFactory implements TypeAdapterFactory {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Type rawType = type.getRawType();
if (rawType != List.class) {
return null;
}
Type elementType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) new TypeAdapter<List<?>>() {
@Override
public void write(JsonWriter out, List<?> list) throws IOException {
if (list == null) {
out.nullValue();
return;
}
out.beginArray();
for (Object element : list) {
elementAdapter.write(out, element);
}
out.endArray();
}
@Override
public List<?> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
List<Object> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
list.add(elementAdapter.read(in));
}
in.endArray();
return list;
}
};
}
}
```
### Delegating Adapters
Sometimes you need to modify the behavior of an existing adapter:
```java
public class TimestampAdapter extends TypeAdapter<Date> {
private final TypeAdapter<Date> delegate;
public TimestampAdapter(TypeAdapter<Date> delegate) {
this.delegate = delegate;
}
@Override
public void write(JsonWriter out, Date date) throws IOException {
if (date == null) {
out.nullValue();
} else {
out.value(date.getTime()); // Write as timestamp
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
long timestamp = in.nextLong();
return new Date(timestamp);
}
}
```
### Hierarchical Type Adapters
Handle inheritance hierarchies:
```java
Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(Animal.class, new AnimalAdapter())
.create();
// This adapter will be used for Animal and all its subclasses
public class AnimalAdapter implements JsonSerializer<Animal>, JsonDeserializer<Animal> {
@Override
public JsonElement serialize(Animal src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("type", new JsonPrimitive(src.getClass().getSimpleName()));
result.add("properties", context.serialize(src, src.getClass()));
return result;
}
@Override
public Animal deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String type = jsonObject.get("type").getAsString();
JsonElement element = jsonObject.get("properties");
try {
Class<?> clazz = Class.forName("com.example." + type);
return context.deserialize(element, clazz);
} catch (ClassNotFoundException e) {
throw new JsonParseException("Unknown element type: " + type, e);
}
}
}
```
## Null Handling
Type adapters can handle nulls explicitly or use the `nullSafe()` wrapper:
```java
public class NullSafeStringAdapter extends TypeAdapter<String> {
@Override
public void write(JsonWriter out, String value) throws IOException {
out.value(value == null ? "NULL" : value);
}
@Override
public String read(JsonReader in) throws IOException {
String value = in.nextString();
return "NULL".equals(value) ? null : value;
}
}
// Or use nullSafe wrapper
TypeAdapter<String> adapter = new StringAdapter().nullSafe();
```
## Accessing Delegate Adapters
Get the next adapter in the chain to avoid infinite recursion:
```java
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new LoggingAdapterFactory())
.create();
public class LoggingAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
System.out.println("Serializing: " + value);
delegate.write(out, value);
}
@Override
public T read(JsonReader in) throws IOException {
T result = delegate.read(in);
System.out.println("Deserialized: " + result);
return result;
}
};
}
}
```
## Legacy Serializer/Deserializer Interfaces
For simpler cases, you can use the legacy interfaces:
```java { .api }
public interface JsonSerializer<T> {
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
public interface JsonDeserializer<T> {
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException;
}
```
**Context interfaces:**
```java { .api }
public interface JsonSerializationContext {
public JsonElement serialize(Object src);
public JsonElement serialize(Object src, Type typeOfSrc);
}
public interface JsonDeserializationContext {
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
}
```
**Usage:**
```java
public class PersonSerializer implements JsonSerializer<Person> {
@Override
public JsonElement serialize(Person src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("fullName", src.getFirstName() + " " + src.getLastName());
obj.addProperty("age", src.getAge());
return obj;
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(Person.class, new PersonSerializer())
.create();
```

View file

@ -0,0 +1,7 @@
{
"name": "tessl/maven-com-google-code-gson--gson",
"version": "2.13.0",
"docs": "docs/index.md",
"describes": "pkg:maven/com.google.code.gson/gson@2.13.1",
"summary": "Java library for converting Java objects to JSON and vice-versa."
}

View file

@ -0,0 +1,200 @@
# JaCoCo Agent
JaCoCo Agent provides programmatic access to the JaCoCo runtime agent JAR file for Java code coverage analysis. This module serves as a wrapper and resource provider for the jacocoagent.jar file, offering APIs to extract, access, and deploy the coverage agent in various Java environments.
## Package Information
- **Package Name**: org.jacoco.agent
- **Package Type**: maven
- **Language**: Java
- **Installation**:
```xml
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>0.8.13</version>
</dependency>
```
## Core Imports
```java
import org.jacoco.agent.AgentJar;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
```
## Basic Usage
```java
import org.jacoco.agent.AgentJar;
import java.io.File;
import java.io.IOException;
// Get the agent JAR as a URL
URL agentUrl = AgentJar.getResource();
// Get the agent JAR as an InputStream
InputStream agentStream = AgentJar.getResourceAsStream();
// Extract agent to a temporary location
File tempAgent = AgentJar.extractToTempLocation();
// Extract agent to a specific location
File specificLocation = new File("/path/to/jacocoagent.jar");
AgentJar.extractTo(specificLocation);
```
## Capabilities
### Agent Resource Access
Access the embedded JaCoCo agent JAR file as a resource.
```java { .api }
/**
* Returns a URL pointing to the JAR file.
* @return URL of the JAR file
*/
public static URL getResource();
/**
* Returns the content of the JAR file as a stream.
* @return content of the JAR file
*/
public static InputStream getResourceAsStream();
```
**Usage Examples:**
```java
// Access via URL
URL agentUrl = AgentJar.getResource();
InputStream stream = agentUrl.openStream();
// Direct stream access with proper resource management
try (InputStream agentStream = AgentJar.getResourceAsStream()) {
// Use the stream for processing
// Stream is automatically closed when try block exits
}
```
### Agent Extraction
Extract the embedded agent JAR to file system locations.
```java { .api }
/**
* Extract the JaCoCo agent JAR and put it into a temporary location. This
* file should be deleted on exit, but may not if the VM is terminated
* @return Location of the Agent Jar file in the local file system. The file
* should exist and be readable.
* @throws IOException Unable to unpack agent jar
*/
public static File extractToTempLocation() throws IOException;
/**
* Extract the JaCoCo agent JAR and put it into the specified location.
* @param destination Location to write JaCoCo Agent Jar to. Must be writeable
* @throws IOException Unable to unpack agent jar
*/
public static void extractTo(File destination) throws IOException;
```
**Usage Examples:**
```java
// Extract to temporary location (automatically deleted on JVM exit)
File tempAgentFile = AgentJar.extractToTempLocation();
System.out.println("Agent extracted to: " + tempAgentFile.getAbsolutePath());
// Extract to specific location
File agentFile = new File("./jacocoagent.jar");
try {
AgentJar.extractTo(agentFile);
System.out.println("Agent extracted to: " + agentFile.getAbsolutePath());
} catch (IOException e) {
System.err.println("Failed to extract agent: " + e.getMessage());
}
```
## Types
```java { .api }
public final class AgentJar {
// Private constructor - cannot be instantiated
private AgentJar();
// All methods are static
}
```
## Error Handling
The JaCoCo Agent API uses two main types of exceptions:
- **AssertionError**: Thrown when the embedded `/jacocoagent.jar` resource is not found. This typically indicates a build or packaging issue. The error includes a reference to `/org.jacoco.agent/README.TXT` for troubleshooting details.
- **IOException**: Thrown by extraction methods for I/O related failures, such as:
- Destination file is not writable
- Destination path does not exist
- Insufficient disk space
- File system permissions issues
**Error Handling Example:**
```java
try {
// Resource access - may throw AssertionError
URL agentUrl = AgentJar.getResource();
// File extraction - may throw IOException
File agentFile = new File("./jacocoagent.jar");
AgentJar.extractTo(agentFile);
} catch (AssertionError e) {
System.err.println("Agent resource not found. Check build configuration.");
} catch (IOException e) {
System.err.println("Failed to extract agent: " + e.getMessage());
}
```
## Key Characteristics
- **Utility Class**: AgentJar is a final class with only static methods and private constructor (cannot be instantiated)
- **Resource Provider**: Acts as a wrapper around the embedded `/jacocoagent.jar` resource within the JAR file
- **Thread Safety**: All methods are static and thread-safe
- **Self-Contained**: Includes the complete agent JAR as an embedded resource at runtime
- **Build Integration**: The agent JAR is created and embedded during the Maven build process
- **No External Dependencies**: Pure Java implementation using only standard library classes
- **Safe Resource Handling**: Internal implementation uses safe stream closing to prevent resource leaks
## Integration Patterns
Common usage patterns for integrating JaCoCo Agent in applications:
**Testing Framework Integration:**
```java
// Extract agent for use with JVM arguments
File agent = AgentJar.extractToTempLocation();
String javaagentArg = "-javaagent:" + agent.getAbsolutePath();
// Use javaagentArg when launching test JVMs
```
**Build Tool Integration:**
```java
// Extract to build directory for distribution
File buildDir = new File("target/jacoco");
buildDir.mkdirs();
File agentJar = new File(buildDir, "jacocoagent.jar");
AgentJar.extractTo(agentJar);
```
**Runtime Deployment:**
```java
// Use the built-in extraction method for deployment
File deploymentFile = new File("/path/to/deployment/jacocoagent.jar");
AgentJar.extractTo(deploymentFile);
// The extractTo method handles stream management and proper copying
```

View file

@ -0,0 +1,7 @@
{
"name": "tessl/maven-org-jacoco--org-jacoco-agent",
"version": "0.8.0",
"docs": "docs/index.md",
"describes": "pkg:maven/org.jacoco/org.jacoco.agent@0.8.13",
"summary": "JaCoCo Agent provides programmatic access to the JaCoCo runtime agent JAR file for Java code coverage analysis."
}

View file

@ -0,0 +1,535 @@
# Assertions and Assumptions
Comprehensive assertion methods for verifying test conditions and assumptions for conditional test execution. JUnit Jupiter provides a rich set of assertion methods with clear failure messages and support for custom error messages.
## Imports
```java
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
```
## Capabilities
### Basic Assertions
Core assertion methods for common verification scenarios.
```java { .api }
/**
* Assert that two objects are equal
*/
static void assertEquals(Object expected, Object actual);
static void assertEquals(Object expected, Object actual, String message);
static void assertEquals(Object expected, Object actual, Supplier<String> messageSupplier);
/**
* Assert that two objects are not equal
*/
static void assertNotEquals(Object unexpected, Object actual);
static void assertNotEquals(Object unexpected, Object actual, String message);
static void assertNotEquals(Object unexpected, Object actual, Supplier<String> messageSupplier);
/**
* Assert that a condition is true
*/
static void assertTrue(boolean condition);
static void assertTrue(boolean condition, String message);
static void assertTrue(boolean condition, Supplier<String> messageSupplier);
/**
* Assert that a condition is false
*/
static void assertFalse(boolean condition);
static void assertFalse(boolean condition, String message);
static void assertFalse(boolean condition, Supplier<String> messageSupplier);
/**
* Assert that an object is null
*/
static void assertNull(Object actual);
static void assertNull(Object actual, String message);
static void assertNull(Object actual, Supplier<String> messageSupplier);
/**
* Assert that an object is not null
*/
static void assertNotNull(Object actual);
static void assertNotNull(Object actual, String message);
static void assertNotNull(Object actual, Supplier<String> messageSupplier);
```
**Usage Examples:**
```java
@Test
void testBasicAssertions() {
assertEquals(4, 2 + 2, "Simple addition should work");
assertNotEquals(3, 2 + 2);
assertTrue(5 > 3, "5 should be greater than 3");
assertFalse(5 < 3);
String nullString = null;
String nonNullString = "hello";
assertNull(nullString);
assertNotNull(nonNullString, "String should not be null");
}
```
### Reference Assertions
Assert object identity and reference equality.
```java { .api }
/**
* Assert that two objects refer to the same object
*/
static void assertSame(Object expected, Object actual);
static void assertSame(Object expected, Object actual, String message);
static void assertSame(Object expected, Object actual, Supplier<String> messageSupplier);
/**
* Assert that two objects do not refer to the same object
*/
static void assertNotSame(Object unexpected, Object actual);
static void assertNotSame(Object unexpected, Object actual, String message);
static void assertNotSame(Object unexpected, Object actual, Supplier<String> messageSupplier);
```
**Usage Example:**
```java
@Test
void testReferenceAssertions() {
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = str1;
assertEquals(str1, str2); // Content equality
assertNotSame(str1, str2, "Different objects should not be same");
assertSame(str1, str3, "Same reference should be same");
}
```
### Array and Collection Assertions
Specialized assertions for arrays and collections.
```java { .api }
/**
* Assert that two arrays are equal
*/
static void assertArrayEquals(boolean[] expected, boolean[] actual);
static void assertArrayEquals(byte[] expected, byte[] actual);
static void assertArrayEquals(char[] expected, char[] actual);
static void assertArrayEquals(double[] expected, double[] actual);
static void assertArrayEquals(double[] expected, double[] actual, double delta);
static void assertArrayEquals(float[] expected, float[] actual);
static void assertArrayEquals(float[] expected, float[] actual, double delta);
static void assertArrayEquals(int[] expected, int[] actual);
static void assertArrayEquals(long[] expected, long[] actual);
static void assertArrayEquals(Object[] expected, Object[] actual);
static void assertArrayEquals(short[] expected, short[] actual);
/**
* Assert that two iterables are equal
*/
static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual);
static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual, String message);
static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual, Supplier<String> messageSupplier);
/**
* Assert that lines match with pattern support
*/
static void assertLinesMatch(List<String> expectedLines, List<String> actualLines);
static void assertLinesMatch(List<String> expectedLines, List<String> actualLines, String message);
static void assertLinesMatch(List<String> expectedLines, List<String> actualLines, Supplier<String> messageSupplier);
static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines);
static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines, String message);
static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines, Supplier<String> messageSupplier);
```
**Usage Examples:**
```java
@Test
void testArrayAssertions() {
int[] expected = {1, 2, 3};
int[] actual = {1, 2, 3};
assertArrayEquals(expected, actual);
double[] expectedDoubles = {1.1, 2.2, 3.3};
double[] actualDoubles = {1.1, 2.2, 3.3};
assertArrayEquals(expectedDoubles, actualDoubles, 0.01);
}
@Test
void testIterableAssertions() {
List<String> expected = Arrays.asList("a", "b", "c");
List<String> actual = Arrays.asList("a", "b", "c");
assertIterableEquals(expected, actual);
}
@Test
void testLinesMatch() {
List<String> expected = Arrays.asList("Hello.*", "\\d+", "End");
List<String> actual = Arrays.asList("Hello World", "123", "End");
assertLinesMatch(expected, actual);
}
```
### Exception Assertions
Assert that specific exceptions are thrown or not thrown.
```java { .api }
/**
* Assert that executable throws expected exception type
*/
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable);
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, String message);
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier);
/**
* Assert that executable throws exactly the expected exception type
*/
static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable);
static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable, String message);
static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier);
/**
* Assert that executable does not throw any exception
*/
static void assertDoesNotThrow(Executable executable);
static void assertDoesNotThrow(Executable executable, String message);
static void assertDoesNotThrow(Executable executable, Supplier<String> messageSupplier);
static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier);
static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier, String message);
static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier, Supplier<String> messageSupplier);
```
**Usage Examples:**
```java
@Test
void testExceptionAssertions() {
// Assert specific exception is thrown
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> { throw new IllegalArgumentException("Invalid argument"); },
"Should throw IllegalArgumentException"
);
assertEquals("Invalid argument", exception.getMessage());
// Assert exact exception type
RuntimeException exactException = assertThrowsExactly(
RuntimeException.class,
() -> { throw new RuntimeException("Runtime error"); }
);
// Assert no exception is thrown
assertDoesNotThrow(() -> {
String result = "safe operation";
return result;
});
// Assert no exception and return value
String result = assertDoesNotThrow(() -> "safe operation");
assertEquals("safe operation", result);
}
```
### Timeout Assertions
Assert that operations complete within specified time limits.
```java { .api }
/**
* Assert that executable completes within timeout
*/
static void assertTimeout(Duration timeout, Executable executable);
static void assertTimeout(Duration timeout, Executable executable, String message);
static void assertTimeout(Duration timeout, Executable executable, Supplier<String> messageSupplier);
static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier);
static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier, String message);
static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier, Supplier<String> messageSupplier);
/**
* Assert that executable completes within timeout, preemptively aborting if it takes too long
*/
static void assertTimeoutPreemptively(Duration timeout, Executable executable);
static void assertTimeoutPreemptively(Duration timeout, Executable executable, String message);
static void assertTimeoutPreemptively(Duration timeout, Executable executable, Supplier<String> messageSupplier);
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier);
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, String message);
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, Supplier<String> messageSupplier);
```
**Usage Examples:**
```java
@Test
void testTimeoutAssertions() {
// Assert operation completes within timeout
assertTimeout(Duration.ofSeconds(2), () -> {
Thread.sleep(1000); // 1 second delay
});
// Assert with return value
String result = assertTimeout(Duration.ofSeconds(1), () -> {
return "Quick operation";
});
assertEquals("Quick operation", result);
// Preemptive timeout (interrupts if takes too long)
assertTimeoutPreemptively(Duration.ofMillis(500), () -> {
Thread.sleep(100); // Short delay
});
}
```
### Instance Type Assertions
Assert object types and inheritance relationships.
```java { .api }
/**
* Assert that object is instance of expected type
*/
static void assertInstanceOf(Class<?> expectedType, Object actualValue);
static void assertInstanceOf(Class<?> expectedType, Object actualValue, String message);
static void assertInstanceOf(Class<?> expectedType, Object actualValue, Supplier<String> messageSupplier);
static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue);
static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue, String message);
static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue, Supplier<String> messageSupplier);
```
**Usage Example:**
```java
@Test
void testInstanceAssertions() {
Object obj = "Hello World";
// Simple instance check
assertInstanceOf(String.class, obj);
// With type casting
String str = assertInstanceOf(String.class, obj, "Object should be String");
assertEquals(11, str.length());
// Check inheritance
Number num = 42;
assertInstanceOf(Integer.class, num);
}
```
### Grouped Assertions
Execute multiple assertions together and report all failures.
```java { .api }
/**
* Group multiple assertions and execute all, reporting all failures
*/
static void assertAll(Executable... executables);
static void assertAll(String heading, Executable... executables);
static void assertAll(Collection<Executable> executables);
static void assertAll(String heading, Collection<Executable> executables);
static void assertAll(Stream<Executable> executables);
static void assertAll(String heading, Stream<Executable> executables);
```
**Usage Example:**
```java
@Test
void testGroupedAssertions() {
Person person = new Person("John", "Doe", 30);
assertAll("Person properties",
() -> assertEquals("John", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName()),
() -> assertTrue(person.getAge() > 0),
() -> assertNotNull(person.getFullName())
);
// Using collections
List<Executable> assertions = Arrays.asList(
() -> assertEquals(4, 2 + 2),
() -> assertTrue(5 > 3),
() -> assertNotNull("test")
);
assertAll("Math assertions", assertions);
}
```
### Failure Methods
Explicitly fail tests with custom messages.
```java { .api }
/**
* Explicitly fail test
*/
static void fail();
static void fail(String message);
static void fail(String message, Throwable cause);
static void fail(Throwable cause);
static void fail(Supplier<String> messageSupplier);
static <T> T fail();
static <T> T fail(String message);
static <T> T fail(String message, Throwable cause);
static <T> T fail(Throwable cause);
static <T> T fail(Supplier<String> messageSupplier);
```
**Usage Example:**
```java
@Test
void testFailMethods() {
boolean condition = false;
if (!condition) {
fail("Condition was not met");
}
// With supplier for expensive message creation
if (!condition) {
fail(() -> "Complex message: " + generateComplexMessage());
}
// In switch statement
switch (value) {
case 1: /* handle */ break;
case 2: /* handle */ break;
default: fail("Unexpected value: " + value);
}
}
```
### Assumptions
Conditional test execution based on assumptions about the test environment.
```java { .api }
/**
* Assume that a condition is true, abort test if false
*/
static void assumeTrue(boolean assumption);
static void assumeTrue(boolean assumption, String message);
static void assumeTrue(boolean assumption, Supplier<String> messageSupplier);
static void assumeTrue(BooleanSupplier assumptionSupplier);
static void assumeTrue(BooleanSupplier assumptionSupplier, String message);
static void assumeTrue(BooleanSupplier assumptionSupplier, Supplier<String> messageSupplier);
/**
* Assume that a condition is false, abort test if true
*/
static void assumeFalse(boolean assumption);
static void assumeFalse(boolean assumption, String message);
static void assumeFalse(boolean assumption, Supplier<String> messageSupplier);
static void assumeFalse(BooleanSupplier assumptionSupplier);
static void assumeFalse(BooleanSupplier assumptionSupplier, String message);
static void assumeFalse(BooleanSupplier assumptionSupplier, Supplier<String> messageSupplier);
/**
* Execute test code only if assumption is true
*/
static void assumingThat(boolean assumption, Executable executable);
static void assumingThat(BooleanSupplier assumptionSupplier, Executable executable);
```
**Usage Examples:**
```java
@Test
void testOnlyOnLinux() {
assumeTrue(System.getProperty("os.name").toLowerCase().contains("linux"));
// This test will only run on Linux
// Will be skipped (not failed) on other operating systems
assertEquals("/", File.separator);
}
@Test
void testWithPartialAssumption() {
// This part always runs
assertEquals(4, 2 + 2);
// This part only runs if assumption is true
assumingThat(System.getProperty("env").equals("dev"), () -> {
// Development-only test code
assertEquals("localhost", getServerHost());
});
// This part always runs
assertTrue(true);
}
@Test
void testWithEnvironmentCheck() {
String env = System.getProperty("test.env");
assumeFalse("prod".equals(env), "Not running destructive test in production");
// Destructive test that should not run in production
database.deleteAllData();
}
```
### Assertion Failure Builder
Build custom assertion failures with detailed information.
```java { .api }
class AssertionFailureBuilder {
/**
* Create new assertion failure builder
*/
static AssertionFailureBuilder assertionFailure();
/**
* Set failure message
*/
AssertionFailureBuilder message(String message);
/**
* Set expected value
*/
AssertionFailureBuilder expected(Object expected);
/**
* Set actual value
*/
AssertionFailureBuilder actual(Object actual);
/**
* Set cause exception
*/
AssertionFailureBuilder cause(Throwable cause);
/**
* Build the assertion failure
*/
AssertionFailedError build();
}
```
**Usage Example:**
```java
@Test
void testCustomAssertion() {
String expected = "hello";
String actual = "world";
if (!expected.equals(actual)) {
throw AssertionFailureBuilder.assertionFailure()
.message("Strings should match")
.expected(expected)
.actual(actual)
.build();
}
}
```

View file

@ -0,0 +1,438 @@
# Conditional Execution
Rich set of built-in conditions for controlling test execution based on runtime environment, system properties, and custom logic. Tests can be enabled or disabled dynamically based on various criteria.
## Imports
```java
import org.junit.jupiter.api.condition.*;
import static org.junit.jupiter.api.Assertions.*;
```
## Capabilities
### Operating System Conditions
Enable or disable tests based on the operating system.
```java { .api }
/**
* Enable test on specific operating systems
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledOnOsCondition.class)
@interface EnabledOnOs {
OS[] value();
String disabledReason() default "";
}
/**
* Disable test on specific operating systems
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledOnOsCondition.class)
@interface DisabledOnOs {
OS[] value();
String disabledReason() default "";
}
/**
* Operating system enumeration
*/
enum OS {
LINUX,
MAC,
WINDOWS,
AIX,
SOLARIS,
OTHER
}
```
**Usage Examples:**
```java
class OsSpecificTest {
@Test
@EnabledOnOs(OS.LINUX)
void testOnLinuxOnly() {
assertEquals("/", File.separator);
}
@Test
@EnabledOnOs({OS.WINDOWS, OS.MAC})
void testOnWindowsOrMac() {
assertNotEquals("/", File.separator);
}
@Test
@DisabledOnOs(value = OS.WINDOWS, disabledReason = "Windows path handling differs")
void testUnixPaths() {
assertTrue(Paths.get("/usr/local").isAbsolute());
}
}
```
### Java Runtime Environment Conditions
Control execution based on JRE version.
```java { .api }
/**
* Enable test on specific JRE versions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledOnJreCondition.class)
@interface EnabledOnJre {
JRE[] value();
String disabledReason() default "";
}
/**
* Disable test on specific JRE versions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledOnJreCondition.class)
@interface DisabledOnJre {
JRE[] value();
String disabledReason() default "";
}
/**
* Enable test for JRE version ranges
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledForJreRangeCondition.class)
@interface EnabledForJreRange {
JRE min() default JRE.JAVA_8;
JRE max() default JRE.OTHER;
String disabledReason() default "";
}
/**
* Disable test for JRE version ranges
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledForJreRangeCondition.class)
@interface DisabledForJreRange {
JRE min() default JRE.JAVA_8;
JRE max() default JRE.OTHER;
String disabledReason() default "";
}
```
**Usage Examples:**
```java
class JreSpecificTest {
@Test
@EnabledOnJre(JRE.JAVA_8)
void testOnJava8Only() {
// Java 8 specific functionality
}
@Test
@EnabledForJreRange(min = JRE.JAVA_11, max = JRE.JAVA_17)
void testOnJava11To17() {
// Features available in Java 11-17
}
@Test
@DisabledOnJre(value = JRE.JAVA_8, disabledReason = "Lambda syntax not supported")
void testWithModernJavaFeatures() {
// Modern Java features
var list = List.of("item1", "item2");
assertFalse(list.isEmpty());
}
}
```
### System Property Conditions
Execute tests conditionally based on system properties.
```java { .api }
/**
* Enable test if system property matches
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(EnabledIfSystemProperties.class)
@ExtendWith(EnabledIfSystemPropertyCondition.class)
@interface EnabledIfSystemProperty {
String named();
String matches();
String disabledReason() default "";
}
/**
* Container for multiple system property conditions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledIfSystemPropertyCondition.class)
@interface EnabledIfSystemProperties {
EnabledIfSystemProperty[] value();
}
/**
* Disable test if system property matches
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DisabledIfSystemProperties.class)
@ExtendWith(DisabledIfSystemPropertyCondition.class)
@interface DisabledIfSystemProperty {
String named();
String matches();
String disabledReason() default "";
}
/**
* Container for multiple system property conditions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledIfSystemPropertyCondition.class)
@interface DisabledIfSystemProperties {
DisabledIfSystemProperty[] value();
}
```
**Usage Examples:**
```java
class SystemPropertyTest {
@Test
@EnabledIfSystemProperty(named = "env", matches = "dev")
void testInDevelopmentOnly() {
// Development environment specific test
}
@Test
@EnabledIfSystemProperty(named = "debug", matches = "true")
void testWithDebugEnabled() {
// Debug mode specific test
}
@Test
@DisabledIfSystemProperty(named = "ci", matches = "true",
disabledReason = "Flaky in CI environment")
void testDisabledInCI() {
// Test that's unreliable in CI
}
@Test
@EnabledIfSystemProperties({
@EnabledIfSystemProperty(named = "env", matches = "test"),
@EnabledIfSystemProperty(named = "db.enabled", matches = "true")
})
void testWithMultipleProperties() {
// Test requiring multiple system properties
}
}
```
### Environment Variable Conditions
Execute tests conditionally based on environment variables.
```java { .api }
/**
* Enable test if environment variable matches
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(EnabledIfEnvironmentVariables.class)
@ExtendWith(EnabledIfEnvironmentVariableCondition.class)
@interface EnabledIfEnvironmentVariable {
String named();
String matches();
String disabledReason() default "";
}
/**
* Container for multiple environment variable conditions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledIfEnvironmentVariableCondition.class)
@interface EnabledIfEnvironmentVariables {
EnabledIfEnvironmentVariable[] value();
}
/**
* Disable test if environment variable matches
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DisabledIfEnvironmentVariables.class)
@ExtendWith(DisabledIfEnvironmentVariableCondition.class)
@interface DisabledIfEnvironmentVariable {
String named();
String matches();
String disabledReason() default "";
}
/**
* Container for multiple environment variable conditions
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledIfEnvironmentVariableCondition.class)
@interface DisabledIfEnvironmentVariables {
DisabledIfEnvironmentVariable[] value();
}
```
**Usage Examples:**
```java
class EnvironmentVariableTest {
@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches = "production")
void testInProductionOnly() {
// Production-specific test
}
@Test
@EnabledIfEnvironmentVariable(named = "DATABASE_URL", matches = ".*localhost.*")
void testWithLocalDatabase() {
// Test requiring local database
}
@Test
@DisabledIfEnvironmentVariable(named = "SKIP_SLOW_TESTS", matches = "true")
void slowTest() throws InterruptedException {
Thread.sleep(5000);
assertTrue(true);
}
}
```
### Custom Condition Methods
Execute tests based on custom boolean methods.
```java { .api }
/**
* Enable test if custom condition method returns true
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledIfCondition.class)
@interface EnabledIf {
/**
* Method name that returns boolean
*/
String value();
String disabledReason() default "";
}
/**
* Disable test if custom condition method returns true
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledIfCondition.class)
@interface DisabledIf {
/**
* Method name that returns boolean
*/
String value();
String disabledReason() default "";
}
```
**Usage Examples:**
```java
class CustomConditionTest {
@Test
@EnabledIf("isExternalServiceAvailable")
void testWithExternalService() {
// Test that requires external service
}
@Test
@DisabledIf("isWeekend")
void testDisabledOnWeekends() {
// Test that shouldn't run on weekends
}
static boolean isExternalServiceAvailable() {
try {
// Check if external service is reachable
URL url = new URL("http://api.example.com/health");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(1000);
return connection.getResponseCode() == 200;
} catch (Exception e) {
return false;
}
}
static boolean isWeekend() {
DayOfWeek today = LocalDate.now().getDayOfWeek();
return today == DayOfWeek.SATURDAY || today == DayOfWeek.SUNDAY;
}
}
```
### GraalVM Native Image Conditions
Control execution in GraalVM native image contexts.
```java { .api }
/**
* Enable test only in GraalVM native image
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface EnabledInNativeImage {
String disabledReason() default "";
}
/**
* Disable test in GraalVM native image
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface DisabledInNativeImage {
String disabledReason() default "";
}
```
**Usage Examples:**
```java
class NativeImageTest {
@Test
@EnabledInNativeImage
void testNativeImageSpecificBehavior() {
// Test behavior specific to native image
}
@Test
@DisabledInNativeImage(disabledReason = "Reflection not available in native image")
void testWithReflection() {
// Test using reflection APIs
Class<?> clazz = String.class;
Method[] methods = clazz.getDeclaredMethods();
assertTrue(methods.length > 0);
}
}
```

View file

@ -0,0 +1,571 @@
# Core Testing API
Essential testing annotations and lifecycle methods that form the foundation of JUnit Jupiter tests. These provide the basic structure for organizing and executing tests with modern Java features.
## Imports
```java
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
```
## Capabilities
### Test Annotation
Marks a method as a test method that should be executed by the test engine.
```java { .api }
/**
* Marks a method as a test method
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Test {
}
```
**Usage Example:**
```java
class MyTest {
@Test
void shouldCalculateCorrectly() {
// Test implementation
assertEquals(4, 2 + 2);
}
}
```
### Lifecycle Annotations
Control test execution lifecycle with setup and teardown methods.
```java { .api }
/**
* Executed once before all test methods in the class
* Method must be static
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface BeforeAll {
}
/**
* Executed before each individual test method
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface BeforeEach {
}
/**
* Executed after each individual test method
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface AfterEach {
}
/**
* Executed once after all test methods in the class
* Method must be static
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface AfterAll {
}
```
**Usage Example:**
```java
class DatabaseTest {
static Database database;
Connection connection;
@BeforeAll
static void initDatabase() {
database = new Database();
database.start();
}
@AfterAll
static void cleanupDatabase() {
database.stop();
}
@BeforeEach
void openConnection() {
connection = database.openConnection();
}
@AfterEach
void closeConnection() {
if (connection != null) {
connection.close();
}
}
@Test
void testQuery() {
// Test with connection
}
}
```
### Display Names
Customize test names for better readability in test reports.
```java { .api }
/**
* Custom display name for tests and test classes
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface DisplayName {
String value();
}
/**
* Generate display names using a specific strategy
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface DisplayNameGeneration {
Class<? extends DisplayNameGenerator> value();
}
```
**Usage Example:**
```java
@DisplayName("Calculator Tests")
class CalculatorTest {
@Test
@DisplayName("Addition should work for positive numbers")
void testAddition() {
assertEquals(5, 2 + 3);
}
@Test
@DisplayName("Division by zero should throw exception")
void testDivisionByZero() {
assertThrows(ArithmeticException.class, () -> 10 / 0);
}
}
```
### Display Name Generators
Built-in strategies for generating display names automatically.
```java { .api }
interface DisplayNameGenerator {
String generateDisplayNameForClass(Class<?> testClass);
String generateDisplayNameForNestedClass(Class<?> nestedClass);
String generateDisplayNameForMethod(Class<?> testClass, Method testMethod);
class Standard implements DisplayNameGenerator { }
class Simple implements DisplayNameGenerator { }
class ReplaceUnderscores implements DisplayNameGenerator { }
class IndicativeSentences implements DisplayNameGenerator { }
}
```
### Nested Tests
Organize related tests in hierarchical structure using nested classes.
```java { .api }
/**
* Marks a nested class as a test class
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Nested {
}
```
**Usage Example:**
```java
class AccountTest {
@Test
void testCreateAccount() {
// Test account creation
}
@Nested
@DisplayName("When account has balance")
class WhenAccountHasBalance {
Account account;
@BeforeEach
void createAccountWithBalance() {
account = new Account(100);
}
@Test
@DisplayName("withdraw should decrease balance")
void withdrawShouldDecreaseBalance() {
account.withdraw(20);
assertEquals(80, account.getBalance());
}
@Nested
@DisplayName("And withdrawal amount exceeds balance")
class AndWithdrawalExceedsBalance {
@Test
@DisplayName("should throw InsufficientFundsException")
void shouldThrowException() {
assertThrows(InsufficientFundsException.class,
() -> account.withdraw(150));
}
}
}
}
```
### Test Disabling
Disable tests temporarily or conditionally.
```java { .api }
/**
* Disable test execution with optional reason
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Disabled {
String value() default "";
}
```
**Usage Example:**
```java
class FeatureTest {
@Test
@Disabled("Feature not yet implemented")
void testNewFeature() {
// This test won't run
}
@Test
@Disabled
void temporarilyDisabled() {
// This test won't run either
}
}
```
### Test Tagging
Tag tests for filtering and selective execution.
```java { .api }
/**
* Tag a test for filtering
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Tags.class)
@interface Tag {
String value();
}
/**
* Container for multiple tags
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Tags {
Tag[] value();
}
```
**Usage Example:**
```java
class IntegrationTest {
@Test
@Tag("fast")
void testQuickOperation() {
// Fast test
}
@Test
@Tag("slow")
@Tag("integration")
void testDatabaseIntegration() {
// Slow integration test
}
@Test
@Tags({@Tag("smoke"), @Tag("critical")})
void testCriticalPath() {
// Critical smoke test
}
}
```
### Repeated Tests
Execute the same test multiple times with repetition information.
```java { .api }
/**
* Repeat test execution multiple times
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RepeatedTest {
int value();
String name() default "";
}
```
**Usage Example:**
```java
class RandomTest {
@RepeatedTest(10)
void testRandomBehavior() {
int random = (int) (Math.random() * 100);
assertTrue(random >= 0 && random < 100);
}
@RepeatedTest(value = 5, name = "Run {currentRepetition} of {totalRepetitions}")
void testWithCustomName(RepetitionInfo repetitionInfo) {
System.out.println("Repetition: " + repetitionInfo.getCurrentRepetition());
}
}
```
### Test Information
Access test metadata at runtime.
```java { .api }
interface TestInfo {
String getDisplayName();
Set<String> getTags();
Optional<Class<?>> getTestClass();
Optional<Method> getTestMethod();
}
interface RepetitionInfo {
int getCurrentRepetition();
int getTotalRepetitions();
}
```
**Usage Example:**
```java
class InfoTest {
@Test
void testWithInfo(TestInfo testInfo) {
System.out.println("Test name: " + testInfo.getDisplayName());
System.out.println("Tags: " + testInfo.getTags());
}
@RepeatedTest(3)
void repeatedTestWithInfo(RepetitionInfo repetitionInfo) {
System.out.println("Repetition " + repetitionInfo.getCurrentRepetition()
+ " of " + repetitionInfo.getTotalRepetitions());
}
}
```
### Test Ordering
Control the order of test execution.
```java { .api }
/**
* Configure test method execution order
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TestMethodOrder {
Class<? extends MethodOrderer> value();
}
/**
* Configure test class execution order
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TestClassOrder {
Class<? extends ClassOrderer> value();
}
/**
* Specify execution order
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Order {
int value();
}
```
**Usage Example:**
```java
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class OrderedTest {
@Test
@Order(3)
void testThird() {
// Runs third
}
@Test
@Order(1)
void testFirst() {
// Runs first
}
@Test
@Order(2)
void testSecond() {
// Runs second
}
}
```
### Test Instance Lifecycle
Control how test instances are created and managed.
```java { .api }
/**
* Configure test instance lifecycle
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TestInstance {
Lifecycle value();
enum Lifecycle {
PER_METHOD, // Default: new instance per test method
PER_CLASS // One instance per test class
}
}
```
**Usage Example:**
```java
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SharedStateTest {
int counter = 0;
@Test
void firstTest() {
counter++;
assertEquals(1, counter);
}
@Test
void secondTest() {
counter++;
assertEquals(2, counter); // Works because same instance
}
}
```
### Test Timeouts
Configure execution timeouts for individual tests or entire test classes.
```java { .api }
/**
* Configure test execution timeout
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Timeout {
/**
* Timeout value
*/
long value();
/**
* Time unit for timeout value
*/
TimeUnit unit() default TimeUnit.SECONDS;
/**
* Thread mode for timeout enforcement
*/
ThreadMode threadMode() default ThreadMode.SAME_THREAD;
enum ThreadMode {
/**
* Execute in same thread with timeout monitoring
*/
SAME_THREAD,
/**
* Execute in separate thread and interrupt on timeout
*/
SEPARATE_THREAD
}
}
```
**Usage Examples:**
```java
class TimeoutTest {
@Test
@Timeout(5) // 5 seconds
void testWithTimeout() throws InterruptedException {
Thread.sleep(1000); // Will pass
}
@Test
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
void testWithMillisecondTimeout() {
// Test must complete within 500ms
performQuickOperation();
}
@Test
@Timeout(value = 10, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
void testWithSeparateThread() throws InterruptedException {
// Will be interrupted after 10 seconds if still running
Thread.sleep(5000);
}
}
@Timeout(30) // Default 30 second timeout for all tests in class
class SlowTestsWithTimeout {
@Test
void slowTest1() throws InterruptedException {
Thread.sleep(10000); // 10 seconds - within class timeout
}
@Test
@Timeout(60) // Override class timeout for this test
void verySlowTest() throws InterruptedException {
Thread.sleep(45000); // 45 seconds - within method timeout
}
}
```

View file

@ -0,0 +1,395 @@
# Dynamic Tests
Runtime test generation capabilities that allow creating tests programmatically during execution. Dynamic tests enable flexible test scenarios based on runtime data and conditions.
## Imports
```java
import org.junit.jupiter.api.*;
import java.util.stream.Stream;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
```
## Capabilities
### Test Factory Annotation
Mark methods that generate dynamic tests at runtime.
```java { .api }
/**
* Marks a method as a factory for dynamic tests
* Method must return Stream, Collection, Iterable, or Iterator of DynamicNode
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface TestFactory {
}
```
### Dynamic Test Creation
Create individual dynamic test instances.
```java { .api }
/**
* A dynamically generated test
*/
class DynamicTest extends DynamicNode {
/**
* Create a dynamic test with display name and executable
*/
static DynamicTest dynamicTest(String displayName, Executable executable);
/**
* Create a dynamic test with display name, URI, and executable
*/
static DynamicTest dynamicTest(String displayName, URI testSourceUri, Executable executable);
/**
* Create a stream of dynamic tests from input data
*/
static <T> Stream<DynamicTest> stream(Iterator<T> inputGenerator,
Function<? super T, String> displayNameGenerator,
ThrowingConsumer<? super T> testExecutor);
/**
* Create a stream of dynamic tests from input stream
*/
static <T> Stream<DynamicTest> stream(Stream<T> inputStream,
Function<? super T, String> displayNameGenerator,
ThrowingConsumer<? super T> testExecutor);
}
```
**Usage Examples:**
```java
class DynamicTestExample {
@TestFactory
Stream<DynamicTest> simpleTests() {
return Stream.of("apple", "banana", "cherry")
.map(fruit -> DynamicTest.dynamicTest(
"test " + fruit,
() -> {
assertNotNull(fruit);
assertTrue(fruit.length() > 3);
}
));
}
@TestFactory
Collection<DynamicTest> numbersTest() {
return Arrays.asList(
DynamicTest.dynamicTest("Test 1", () -> assertEquals(2, 1 + 1)),
DynamicTest.dynamicTest("Test 2", () -> assertEquals(4, 2 * 2)),
DynamicTest.dynamicTest("Test 3", () -> assertEquals(9, 3 * 3))
);
}
@TestFactory
Stream<DynamicTest> dataStreamTests() {
List<String> data = Arrays.asList("alpha", "beta", "gamma");
return DynamicTest.stream(
data.stream(),
name -> "Processing " + name,
value -> {
assertNotNull(value);
assertTrue(value.length() > 3);
assertFalse(value.isEmpty());
}
);
}
}
```
### Dynamic Container Creation
Group related dynamic tests in containers.
```java { .api }
/**
* A container for dynamic tests or other containers
*/
class DynamicContainer extends DynamicNode {
/**
* Create a dynamic container with display name and children
*/
static DynamicContainer dynamicContainer(String displayName, Stream<DynamicNode> children);
/**
* Create a dynamic container with display name, URI, and children
*/
static DynamicContainer dynamicContainer(String displayName, URI testSourceUri, Stream<DynamicNode> children);
/**
* Create a dynamic container with display name and iterable children
*/
static DynamicContainer dynamicContainer(String displayName, Iterable<DynamicNode> children);
/**
* Create a dynamic container with display name, URI, and iterable children
*/
static DynamicContainer dynamicContainer(String displayName, URI testSourceUri, Iterable<DynamicNode> children);
}
```
**Usage Examples:**
```java
class DynamicContainerExample {
@TestFactory
Stream<DynamicNode> nestedTests() {
return Stream.of(
DynamicContainer.dynamicContainer("String Tests", Stream.of(
DynamicTest.dynamicTest("Test empty string", () -> assertTrue("".isEmpty())),
DynamicTest.dynamicTest("Test non-empty string", () -> assertFalse("hello".isEmpty()))
)),
DynamicContainer.dynamicContainer("Number Tests", Stream.of(
DynamicTest.dynamicTest("Test positive", () -> assertTrue(5 > 0)),
DynamicTest.dynamicTest("Test negative", () -> assertTrue(-5 < 0))
))
);
}
@TestFactory
Stream<DynamicNode> hierarchicalTests() {
return Stream.of("Category A", "Category B")
.map(category -> DynamicContainer.dynamicContainer(
category,
IntStream.range(1, 4)
.mapToObj(i -> DynamicTest.dynamicTest(
category + " Test " + i,
() -> {
assertNotNull(category);
assertTrue(i > 0);
}
))
));
}
}
```
### Dynamic Node Base
Base class for all dynamic test elements.
```java { .api }
/**
* Base class for dynamic tests and containers
*/
abstract class DynamicNode {
/**
* Get display name
*/
String getDisplayName();
/**
* Get test source URI
*/
Optional<URI> getTestSourceUri();
}
```
### Named Interface
Interface for providing names to test components.
```java { .api }
/**
* Interface for named test components
*/
interface Named {
/**
* Get the name
*/
String getName();
/**
* Create Named instance with given name and payload
*/
static <T> Named<T> of(String name, T payload);
}
/**
* Named executable for dynamic test creation
*/
interface NamedExecutable extends Named {
/**
* Get the executable
*/
Executable getExecutable();
/**
* Create NamedExecutable with name and executable
*/
static NamedExecutable of(String name, Executable executable);
}
```
**Usage Examples:**
```java
class NamedTestExample {
@TestFactory
Stream<DynamicTest> namedTests() {
List<Named<String>> testData = Arrays.asList(
Named.of("First test", "alpha"),
Named.of("Second test", "beta"),
Named.of("Third test", "gamma")
);
return testData.stream()
.map(namedData -> DynamicTest.dynamicTest(
namedData.getName(),
() -> {
String value = namedData.getPayload();
assertNotNull(value);
assertTrue(value.length() > 3);
}
));
}
@TestFactory
Stream<DynamicTest> namedExecutableTests() {
List<NamedExecutable> executables = Arrays.asList(
NamedExecutable.of("Test addition", () -> assertEquals(4, 2 + 2)),
NamedExecutable.of("Test subtraction", () -> assertEquals(0, 2 - 2)),
NamedExecutable.of("Test multiplication", () -> assertEquals(4, 2 * 2))
);
return executables.stream()
.map(namedExec -> DynamicTest.dynamicTest(
namedExec.getName(),
namedExec.getExecutable()
));
}
}
```
### Complex Dynamic Test Scenarios
Advanced patterns for dynamic test generation.
**Database-driven Tests:**
```java
class DatabaseDrivenTests {
@TestFactory
Stream<DynamicTest> testAllUsers() {
UserRepository repository = new UserRepository();
return repository.findAll().stream()
.map(user -> DynamicTest.dynamicTest(
"Validate user: " + user.getUsername(),
() -> {
assertNotNull(user.getEmail());
assertTrue(user.getAge() >= 0);
assertFalse(user.getUsername().isEmpty());
}
));
}
@TestFactory
Stream<DynamicNode> testUsersByRole() {
UserRepository repository = new UserRepository();
Map<String, List<User>> usersByRole = repository.findAll().stream()
.collect(Collectors.groupingBy(User::getRole));
return usersByRole.entrySet().stream()
.map(entry -> DynamicContainer.dynamicContainer(
"Role: " + entry.getKey(),
entry.getValue().stream()
.map(user -> DynamicTest.dynamicTest(
"Test " + user.getUsername(),
() -> validateUserInRole(user, entry.getKey())
))
));
}
private void validateUserInRole(User user, String expectedRole) {
assertEquals(expectedRole, user.getRole());
assertNotNull(user.getPermissions());
assertFalse(user.getPermissions().isEmpty());
}
}
```
**Configuration-based Tests:**
```java
class ConfigurationBasedTests {
@TestFactory
Stream<DynamicTest> testConfigurations() throws IOException {
Properties configs = loadTestConfigurations();
return configs.entrySet().stream()
.map(entry -> DynamicTest.dynamicTest(
"Test config: " + entry.getKey(),
() -> {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
assertNotNull(value, "Configuration value should not be null");
validateConfigurationValue(key, value);
}
));
}
@TestFactory
Stream<DynamicNode> testConfigurationGroups() throws IOException {
Map<String, Properties> configGroups = loadConfigurationGroups();
return configGroups.entrySet().stream()
.map(group -> DynamicContainer.dynamicContainer(
"Config Group: " + group.getKey(),
group.getValue().entrySet().stream()
.map(config -> DynamicTest.dynamicTest(
"Test " + config.getKey(),
() -> validateConfigurationValue(
(String) config.getKey(),
(String) config.getValue()
)
))
));
}
private Properties loadTestConfigurations() throws IOException {
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/test-config.properties"));
return props;
}
private Map<String, Properties> loadConfigurationGroups() {
// Load different configuration groups
return Map.of(
"database", loadDatabaseConfigs(),
"security", loadSecurityConfigs(),
"performance", loadPerformanceConfigs()
);
}
private void validateConfigurationValue(String key, String value) {
switch (key) {
case "timeout":
assertTrue(Integer.parseInt(value) > 0);
break;
case "url":
assertTrue(value.startsWith("http"));
break;
default:
assertNotNull(value);
}
}
}
```

View file

@ -0,0 +1,849 @@
# Extensions and Lifecycle
JUnit Jupiter's powerful extension model allows customizing test behavior, dependency injection, and integration with external frameworks. Extensions provide hooks into the test lifecycle and enable sophisticated test customizations.
## Imports
```java
import org.junit.jupiter.api.extension.*;
import static org.junit.jupiter.api.Assertions.*;
```
## Capabilities
### Extension Registration
Register extensions declaratively or programmatically.
```java { .api }
/**
* Register extensions declaratively on test classes and methods
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ExtendWith.List.class)
@interface ExtendWith {
/**
* Extension classes to register
*/
Class<? extends Extension>[] value();
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface List {
ExtendWith[] value();
}
}
/**
* Register extension instance via static field
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface RegisterExtension {
}
/**
* Base extension interface - marker interface for all extensions
*/
interface Extension {
}
```
**Usage Examples:**
```java
// Declarative registration
@ExtendWith(DatabaseExtension.class)
@ExtendWith(MockitoExtension.class)
class MyTest {
@Test
void testWithExtensions() {
// Extensions are active
}
}
// Programmatic registration
class MyTest {
@RegisterExtension
static DatabaseExtension database = new DatabaseExtension("testdb");
@RegisterExtension
MockServerExtension mockServer = new MockServerExtension(8080);
@Test
void testWithProgrammaticExtensions() {
// Extensions configured and active
}
}
```
### Lifecycle Callback Extensions
Hook into test lifecycle events.
```java { .api }
/**
* Callback before all tests in container
*/
interface BeforeAllCallback extends Extension {
void beforeAll(ExtensionContext context) throws Exception;
}
/**
* Callback before each test method
*/
interface BeforeEachCallback extends Extension {
void beforeEach(ExtensionContext context) throws Exception;
}
/**
* Callback before test method execution (after @BeforeEach)
*/
interface BeforeTestExecutionCallback extends Extension {
void beforeTestExecution(ExtensionContext context) throws Exception;
}
/**
* Callback after test method execution (before @AfterEach)
*/
interface AfterTestExecutionCallback extends Extension {
void afterTestExecution(ExtensionContext context) throws Exception;
}
/**
* Callback after each test method
*/
interface AfterEachCallback extends Extension {
void afterEach(ExtensionContext context) throws Exception;
}
/**
* Callback after all tests in container
*/
interface AfterAllCallback extends Extension {
void afterAll(ExtensionContext context) throws Exception;
}
```
**Usage Example:**
```java
class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
private static final String START_TIME = "start time";
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
getStore(context).put(START_TIME, System.currentTimeMillis());
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
Method testMethod = context.getRequiredTestMethod();
long startTime = getStore(context).remove(START_TIME, long.class);
long duration = System.currentTimeMillis() - startTime;
System.out.printf("Method [%s] took %s ms.%n", testMethod.getName(), duration);
}
private ExtensionContext.Store getStore(ExtensionContext context) {
return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
@ExtendWith(TimingExtension.class)
class TimedTest {
@Test
void testThatTakesTime() throws InterruptedException {
Thread.sleep(100);
assertTrue(true);
}
}
```
### Parameter Resolution Extensions
Inject custom parameters into test methods and constructors.
```java { .api }
/**
* Resolve parameters for test methods and constructors
*/
interface ParameterResolver extends Extension {
/**
* Check if this resolver supports the parameter
*/
boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException;
/**
* Resolve the parameter value
*/
Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException;
}
/**
* Parameter context information
*/
interface ParameterContext {
Parameter getParameter();
int getIndex();
Optional<Object> getTarget();
boolean isAnnotated(Class<? extends Annotation> annotationType);
<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);
<A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType);
}
/**
* Type-based parameter resolver base class
*/
abstract class TypeBasedParameterResolver<T> implements ParameterResolver {
private final Class<T> parameterType;
protected TypeBasedParameterResolver(Class<T> parameterType) {
this.parameterType = parameterType;
}
@Override
public final boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterType.equals(parameterContext.getParameter().getType());
}
@Override
public final Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return resolveParameter(extensionContext);
}
/**
* Resolve parameter of the supported type
*/
public abstract T resolveParameter(ExtensionContext extensionContext);
}
```
**Usage Examples:**
```java
class DatabaseConnectionResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == Connection.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return createDatabaseConnection();
}
private Connection createDatabaseConnection() {
// Create and return database connection
return DriverManager.getConnection("jdbc:h2:mem:test");
}
}
class TempDirectoryResolver extends TypeBasedParameterResolver<Path> {
public TempDirectoryResolver() {
super(Path.class);
}
@Override
public Path resolveParameter(ExtensionContext extensionContext) {
return Files.createTempDirectory("junit-test");
}
}
@ExtendWith({DatabaseConnectionResolver.class, TempDirectoryResolver.class})
class DatabaseTest {
@Test
void testWithInjectedParameters(Connection connection, Path tempDir) {
assertNotNull(connection);
assertNotNull(tempDir);
assertTrue(Files.exists(tempDir));
}
}
```
### Conditional Execution Extensions
Control when tests should be executed.
```java { .api }
/**
* Determine whether test should be executed
*/
interface ExecutionCondition extends Extension {
/**
* Evaluate execution condition
*/
ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context);
}
/**
* Result of condition evaluation
*/
class ConditionEvaluationResult {
/**
* Create enabled result
*/
static ConditionEvaluationResult enabled(String reason);
/**
* Create disabled result
*/
static ConditionEvaluationResult disabled(String reason);
/**
* Check if execution is disabled
*/
boolean isDisabled();
/**
* Get reason for the result
*/
Optional<String> getReason();
}
```
**Usage Example:**
```java
class SystemPropertyCondition implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
Optional<SystemProperty> annotation = context.getElement()
.map(element -> element.getAnnotation(SystemProperty.class));
if (annotation.isPresent()) {
SystemProperty systemProperty = annotation.get();
String actualValue = System.getProperty(systemProperty.name());
if (systemProperty.value().equals(actualValue)) {
return ConditionEvaluationResult.enabled("System property matches");
} else {
return ConditionEvaluationResult.disabled(
String.format("System property [%s] does not match expected value [%s]",
systemProperty.name(), systemProperty.value()));
}
}
return ConditionEvaluationResult.enabled("No system property condition");
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SystemPropertyCondition.class)
@interface SystemProperty {
String name();
String value();
}
class ConditionalTest {
@Test
@SystemProperty(name = "env", value = "test")
void testOnlyInTestEnvironment() {
// Only runs when system property env=test
assertTrue(true);
}
}
```
### Test Instance Extensions
Control test instance creation and lifecycle.
```java { .api }
/**
* Create test instances
*/
interface TestInstanceFactory extends Extension {
/**
* Create test instance
*/
Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext)
throws TestInstantiationException;
}
/**
* Test instance factory context
*/
interface TestInstanceFactoryContext {
Class<?> getTestClass();
Optional<Object> getOuterInstance();
}
/**
* Callback before test instance construction
*/
interface TestInstancePreConstructCallback extends Extension {
void preConstructTestInstance(TestInstancePreConstructContext context, ExtensionContext extensionContext);
}
/**
* Process test instance after construction
*/
interface TestInstancePostProcessor extends Extension {
void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception;
}
/**
* Callback before test instance destruction
*/
interface TestInstancePreDestroyCallback extends Extension {
void preDestroyTestInstance(ExtensionContext context) throws Exception;
}
```
**Usage Example:**
```java
class DependencyInjectionExtension implements TestInstancePostProcessor {
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
Class<?> testClass = testInstance.getClass();
for (Field field : testClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
field.set(testInstance, createDependency(field.getType()));
}
}
}
private Object createDependency(Class<?> type) {
// Create dependency instance
if (type == UserService.class) {
return new UserService();
}
return null;
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}
@ExtendWith(DependencyInjectionExtension.class)
class ServiceTest {
@Inject
private UserService userService;
@Test
void testWithInjectedService() {
assertNotNull(userService);
// Use injected service
}
}
```
### Exception Handling Extensions
Handle test execution exceptions.
```java { .api }
/**
* Handle exceptions thrown during test execution
*/
interface TestExecutionExceptionHandler extends Extension {
/**
* Handle test execution exception
*/
void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable;
}
/**
* Callback before test interruption
*/
interface PreInterruptCallback extends Extension {
/**
* Called before test thread is interrupted
*/
void preInterrupt(PreInterruptContext context, ExtensionContext extensionContext) throws Exception;
}
/**
* Pre-interrupt context information
*/
interface PreInterruptContext {
Thread getThreadToInterrupt();
Optional<String> getReason();
}
/**
* Watch test execution and results
*/
interface TestWatcher extends Extension {
/**
* Called when test is disabled
*/
default void testDisabled(ExtensionContext context, Optional<String> reason) {}
/**
* Called when test succeeds
*/
default void testSuccessful(ExtensionContext context) {}
/**
* Called when test is aborted
*/
default void testAborted(ExtensionContext context, Throwable cause) {}
/**
* Called when test fails
*/
default void testFailed(ExtensionContext context, Throwable cause) {}
}
/**
* Intercept method invocations
*/
interface InvocationInterceptor extends Extension {
/**
* Intercept test method invocation
*/
default void interceptTestMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
invocation.proceed();
}
/**
* Intercept test class constructor invocation
*/
default <T> T interceptTestClassConstructor(Invocation<T> invocation,
ReflectiveInvocationContext<Constructor<T>> invocationContext,
ExtensionContext extensionContext) throws Throwable {
return invocation.proceed();
}
/**
* Intercept BeforeAll method invocation
*/
default void interceptBeforeAllMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
invocation.proceed();
}
/**
* Intercept BeforeEach method invocation
*/
default void interceptBeforeEachMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
invocation.proceed();
}
/**
* Intercept AfterEach method invocation
*/
default void interceptAfterEachMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
invocation.proceed();
}
/**
* Intercept AfterAll method invocation
*/
default void interceptAfterAllMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
invocation.proceed();
}
}
```
**Usage Example:**
```java
class RetryExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
Optional<Retry> retryAnnotation = context.getElement()
.map(element -> element.getAnnotation(Retry.class));
if (retryAnnotation.isPresent()) {
int maxRetries = retryAnnotation.get().value();
ExtensionContext.Store store = getStore(context);
int retryCount = store.getOrComputeIfAbsent("retryCount", key -> 0, Integer.class);
if (retryCount < maxRetries) {
store.put("retryCount", retryCount + 1);
// Retry the test by not re-throwing the exception
return;
}
}
// Re-throw if no retry or max retries reached
throw throwable;
}
private ExtensionContext.Store getStore(ExtensionContext context) {
return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(RetryExtension.class)
@interface Retry {
int value() default 3;
}
class FlakeyTest {
@Test
@Retry(5)
void testThatMightFail() {
if (Math.random() < 0.7) {
throw new RuntimeException("Random failure");
}
assertTrue(true);
}
}
```
### Extension Context and Store
Access test context and store data across extension callbacks.
```java { .api }
/**
* Extension execution context
*/
interface ExtensionContext {
/**
* Get parent context
*/
Optional<ExtensionContext> getParent();
/**
* Get root context
*/
ExtensionContext getRoot();
/**
* Get unique ID
*/
String getUniqueId();
/**
* Get display name
*/
String getDisplayName();
/**
* Get all tags
*/
Set<String> getTags();
/**
* Get annotated element (class or method)
*/
Optional<AnnotatedElement> getElement();
/**
* Get test class
*/
Optional<Class<?>> getTestClass();
/**
* Get required test class (throws if not present)
*/
Class<?> getRequiredTestClass();
/**
* Get test instance lifecycle
*/
Optional<TestInstance.Lifecycle> getTestInstanceLifecycle();
/**
* Get test instance (may be null for static methods)
*/
Optional<Object> getTestInstance();
/**
* Get all test instances for nested tests
*/
Optional<TestInstances> getTestInstances();
/**
* Get test method
*/
Optional<Method> getTestMethod();
/**
* Get required test method (throws if not present)
*/
Method getRequiredTestMethod();
/**
* Get execution exception if test failed
*/
Optional<Throwable> getExecutionException();
/**
* Get configuration parameter
*/
Optional<String> getConfigurationParameter(String key);
/**
* Get store for sharing data
*/
Store getStore(Namespace namespace);
/**
* Publish entry to test report
*/
void publishReportEntry(Map<String, String> map);
void publishReportEntry(String key, String value);
/**
* Store namespace for organizing data
*/
class Namespace {
static Namespace create(Object... parts);
static final Namespace GLOBAL;
}
/**
* Key-value store for extension data
*/
interface Store {
Object get(Object key);
<V> V get(Object key, Class<V> requiredType);
<K, V> Object getOrComputeIfAbsent(K key, Function<K, V> defaultCreator);
<K, V> V getOrComputeIfAbsent(K key, Function<K, V> defaultCreator, Class<V> requiredType);
void put(Object key, Object value);
Object remove(Object key);
<V> V remove(Object key, Class<V> requiredType);
<K, V> V getOrDefault(K key, Class<V> requiredType, V defaultValue);
void clear();
interface CloseableResource {
void close() throws Throwable;
}
}
}
```
**Usage Example:**
```java
class DataSharingExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
// Store shared data for all tests
ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.GLOBAL);
store.put("sharedData", new SharedTestData());
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
// Access test-specific information
String testName = context.getDisplayName();
Set<String> tags = context.getTags();
// Store per-test data
ExtensionContext.Store store = getStore(context);
store.put("testStartTime", System.currentTimeMillis());
System.out.printf("Starting test: %s with tags: %s%n", testName, tags);
}
@Override
public void afterAll(ExtensionContext context) throws Exception {
// Cleanup shared data
ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.GLOBAL);
store.remove("sharedData");
}
private ExtensionContext.Store getStore(ExtensionContext context) {
return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
```
## Additional Types
### Invocation and Context Types
Types used with InvocationInterceptor and other advanced extension features.
```java { .api }
/**
* Represents an invocation that can be proceeded
*/
interface Invocation<T> {
/**
* Proceed with the invocation
*/
T proceed() throws Throwable;
/**
* Skip the invocation
*/
void skip();
}
/**
* Context for reflective invocations
*/
interface ReflectiveInvocationContext<T> {
/**
* Get the executable being invoked (Constructor or Method)
*/
T getExecutable();
/**
* Get the arguments for the invocation
*/
List<Object> getArguments();
/**
* Get the target instance (null for static methods/constructors)
*/
Optional<Object> getTarget();
}
/**
* Test instances hierarchy for nested tests
*/
interface TestInstances {
/**
* Get the innermost (most nested) test instance
*/
Object getInnermostInstance();
/**
* Get all test instances from outermost to innermost
*/
List<Object> getEnclosingInstances();
/**
* Get all test instances (enclosing + innermost)
*/
List<Object> getAllInstances();
/**
* Find test instance of specific type
*/
<T> Optional<T> findInstance(Class<T> requiredType);
}
```

View file

@ -0,0 +1,249 @@
# JUnit Jupiter
JUnit Jupiter is the new programming and extension model for JUnit 5, providing a comprehensive testing framework for Java applications. As an aggregator module, it combines the core JUnit Jupiter API, parameterized test support, and the Jupiter test engine to deliver a unified, modern testing experience with advanced features like nested tests, dynamic tests, custom extensions, and parallel execution.
## Package Information
- **Package Name**: org.junit.jupiter:junit-jupiter
- **Package Type**: Maven
- **Language**: Java
- **Installation**: Add to Maven `pom.xml`:
```xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.12.2</version>
<scope>test</scope>
</dependency>
```
Or Gradle `build.gradle`:
```groovy
testImplementation 'org.junit.jupiter:junit-jupiter:5.12.2'
```
## Core Imports
```java
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
```
Common static imports for assertions:
```java
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
```
## Basic Usage
```java
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
@DisplayName("Addition should work correctly")
void testAddition() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
assertNotNull(calc);
}
@BeforeEach
void setUp() {
// Setup before each test
System.out.println("Setting up test");
}
@AfterEach
void tearDown() {
// Cleanup after each test
System.out.println("Cleaning up test");
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testMultipleValues(int value) {
assertTrue(value > 0);
}
}
```
## Architecture
JUnit Jupiter is built around several key components:
- **Test API**: Core annotations and interfaces for writing tests (`@Test`, `@BeforeEach`, etc.)
- **Assertion Engine**: Comprehensive assertion methods with descriptive failure messages
- **Extension Model**: Powerful extension system for custom behavior and integrations
- **Test Engine**: Runtime execution engine that discovers and runs tests
- **Parameter Resolution**: Dependency injection system for test methods and constructors
- **Conditional Execution**: Rich set of conditions for enabling/disabling tests based on environment
## Capabilities
### Core Testing API
Essential testing annotations, lifecycle methods, and basic test structure. Provides the foundation for writing JUnit 5 tests with modern Java features.
```java { .api }
@Test
@BeforeAll
@BeforeEach
@AfterEach
@AfterAll
@DisplayName(String value)
@Nested
@Disabled(String reason)
@Timeout(long value, TimeUnit unit)
```
[Core Testing](./core-testing.md)
### Assertions and Assumptions
Comprehensive assertion methods for verifying test conditions and conditional test execution based on assumptions.
```java { .api }
// Core assertions
static void assertEquals(Object expected, Object actual);
static void assertTrue(boolean condition);
static void assertThrows(Class<T> expectedType, Executable executable);
static void assertAll(Executable... executables);
// Assumptions
static void assumeTrue(boolean assumption);
static void assumingThat(boolean assumption, Executable executable);
```
[Assertions and Assumptions](./assertions.md)
### Parameterized Tests
Advanced parameterized testing with multiple data sources, argument conversion, and aggregation for data-driven test scenarios.
```java { .api }
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
@CsvSource({"1,John", "2,Jane"})
@MethodSource("argumentProvider")
void parameterizedTest(int value, String name);
```
[Parameterized Tests](./parameterized-tests.md)
### Extensions and Lifecycle
Powerful extension model for customizing test behavior, dependency injection, and integrating with external frameworks.
```java { .api }
@ExtendWith(MyExtension.class)
@RegisterExtension
static MyExtension extension = new MyExtension();
interface Extension { }
interface BeforeAllCallback extends Extension;
interface ParameterResolver extends Extension;
```
[Extensions](./extensions.md)
### Conditional Execution
Rich set of conditions for controlling test execution based on operating system, JRE version, system properties, and custom conditions.
```java { .api }
@EnabledOnOs(OS.LINUX)
@DisabledOnJre(JRE.JAVA_8)
@EnabledIfSystemProperty(named = "env", matches = "prod")
@EnabledIf("customCondition")
```
[Conditional Execution](./conditional-execution.md)
### Dynamic Tests
Runtime test generation and nested test organization for complex test scenarios and hierarchical test structure.
```java { .api }
@TestFactory
Stream<DynamicTest> dynamicTests();
static DynamicTest dynamicTest(String displayName, Executable executable);
static DynamicContainer dynamicContainer(String displayName, Stream<DynamicNode> children);
```
[Dynamic Tests](./dynamic-tests.md)
### Parallel Execution and Resource Management
Configuration for parallel test execution, resource locking, and temporary file management for performance optimization.
```java { .api }
@Execution(ExecutionMode.CONCURRENT)
@ResourceLock("database")
@TempDir
Path tempDirectory;
```
[Parallel Execution](./parallel-execution.md)
## Types
### Core Test Interfaces
```java { .api }
interface TestInfo {
String getDisplayName();
Set<String> getTags();
Optional<Class<?>> getTestClass();
Optional<Method> getTestMethod();
}
interface TestReporter {
void publishEntry(Map<String, String> map);
void publishEntry(String key, String value);
}
interface RepetitionInfo {
int getCurrentRepetition();
int getTotalRepetitions();
}
```
### Assertion Utilities
```java { .api }
class AssertionFailureBuilder {
static AssertionFailureBuilder assertionFailure();
AssertionFailureBuilder message(String message);
AssertionFailureBuilder expected(Object expected);
AssertionFailureBuilder actual(Object actual);
AssertionFailedError build();
}
```
### Functional Interfaces
```java { .api }
@FunctionalInterface
interface Executable {
void execute() throws Throwable;
}
@FunctionalInterface
interface ThrowingSupplier<T> {
T get() throws Throwable;
}
@FunctionalInterface
interface ThrowingConsumer<T> {
void accept(T t) throws Throwable;
}
```

View file

@ -0,0 +1,536 @@
# Parallel Execution and Resource Management
Configuration for parallel test execution, resource locking, and temporary file management. JUnit Jupiter provides fine-grained control over test concurrency and resource access.
## Imports
```java
import org.junit.jupiter.api.parallel.*;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.*;
```
## Capabilities
### Parallel Execution Configuration
Control concurrent execution of tests and test classes.
```java { .api }
/**
* Configure parallel execution mode for tests
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Execution {
/**
* Execution mode for this test or test class
*/
ExecutionMode value();
}
/**
* Execution mode enumeration
*/
enum ExecutionMode {
/**
* Execute in same thread as parent
*/
SAME_THREAD,
/**
* Execute concurrently with other tests (if parallel execution enabled)
*/
CONCURRENT
}
```
**Usage Examples:**
```java
// Enable concurrent execution for entire test class
@Execution(ExecutionMode.CONCURRENT)
class ParallelTest {
@Test
void test1() {
// Runs concurrently with other tests
performIndependentOperation();
}
@Test
void test2() {
// Runs concurrently with other tests
performAnotherIndependentOperation();
}
@Test
@Execution(ExecutionMode.SAME_THREAD)
void sequentialTest() {
// Runs sequentially despite class-level concurrent setting
performSequentialOperation();
}
}
// Mixed execution modes
class MixedExecutionTest {
@Test
@Execution(ExecutionMode.CONCURRENT)
void concurrentTest1() {
// Runs concurrently
}
@Test
@Execution(ExecutionMode.CONCURRENT)
void concurrentTest2() {
// Runs concurrently
}
@Test
void defaultTest() {
// Uses default execution mode
}
}
```
### Test Isolation
Force sequential execution for tests that require isolation.
```java { .api }
/**
* Force sequential execution in separate classloader
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Isolated {
}
```
**Usage Example:**
```java
@Isolated
class IsolatedTest {
@Test
void testThatModifiesGlobalState() {
System.setProperty("test.mode", "isolated");
// Test runs in isolation
}
@Test
void anotherIsolatedTest() {
// Also runs in isolation
}
}
class RegularTest {
@Test
@Isolated
void isolatedMethod() {
// Only this method runs in isolation
}
@Test
void regularMethod() {
// Regular execution
}
}
```
### Resource Locking
Coordinate access to shared resources across concurrent tests.
```java { .api }
/**
* Lock access to a shared resource
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ResourceLocks.class)
@interface ResourceLock {
/**
* Resource identifier
*/
String value();
/**
* Access mode for the resource
*/
ResourceAccessMode mode() default ResourceAccessMode.READ_WRITE;
/**
* Target level for the lock
*/
ResourceLockTarget target() default ResourceLockTarget.METHOD;
}
/**
* Container for multiple resource locks
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface ResourceLocks {
ResourceLock[] value();
}
/**
* Resource access mode
*/
enum ResourceAccessMode {
/**
* Exclusive read-write access
*/
READ_WRITE,
/**
* Shared read-only access
*/
READ
}
/**
* Resource lock target level
*/
enum ResourceLockTarget {
/**
* Lock applies to individual method
*/
METHOD,
/**
* Lock applies to entire class
*/
CLASS
}
```
**Usage Examples:**
```java
class ResourceLockTest {
@Test
@ResourceLock("database")
void testDatabaseWrite() {
// Exclusive access to database resource
database.insert("test data");
}
@Test
@ResourceLock(value = "database", mode = ResourceAccessMode.READ)
void testDatabaseRead1() {
// Shared read access - can run concurrently with other read tests
String data = database.select("test data");
assertNotNull(data);
}
@Test
@ResourceLock(value = "database", mode = ResourceAccessMode.READ)
void testDatabaseRead2() {
// Shared read access - can run concurrently with testDatabaseRead1
int count = database.count();
assertTrue(count >= 0);
}
@Test
@ResourceLocks({
@ResourceLock("database"),
@ResourceLock("filesystem")
})
void testMultipleResources() {
// Requires exclusive access to both database and filesystem
database.backup("/tmp/backup");
}
}
@ResourceLock(value = "system-properties", target = ResourceLockTarget.CLASS)
class SystemPropertiesTest {
@Test
void testSystemProperty1() {
System.setProperty("test.prop", "value1");
// Entire class has exclusive access to system properties
}
@Test
void testSystemProperty2() {
System.setProperty("test.prop", "value2");
// Sequential execution guaranteed
}
}
```
### Standard Resources
Pre-defined resource identifiers for common shared resources.
```java { .api }
/**
* Standard resource constants
*/
class Resources {
/**
* Global resource lock
*/
public static final String GLOBAL = "GLOBAL";
/**
* Java system properties
*/
public static final String SYSTEM_PROPERTIES = "SYSTEM_PROPERTIES";
/**
* Java system environment
*/
public static final String SYSTEM_ENVIRONMENT = "SYSTEM_ENVIRONMENT";
/**
* Standard input/output streams
*/
public static final String SYSTEM_OUT = "SYSTEM_OUT";
public static final String SYSTEM_ERR = "SYSTEM_ERR";
public static final String SYSTEM_IN = "SYSTEM_IN";
/**
* Java locale settings
*/
public static final String LOCALE = "LOCALE";
/**
* Java time zone settings
*/
public static final String TIME_ZONE = "TIME_ZONE";
}
```
**Usage Examples:**
```java
class StandardResourcesTest {
@Test
@ResourceLock(Resources.SYSTEM_PROPERTIES)
void testWithSystemProperties() {
String original = System.getProperty("user.dir");
System.setProperty("user.dir", "/tmp");
// Test with modified system property
assertEquals("/tmp", System.getProperty("user.dir"));
// Restore
System.setProperty("user.dir", original);
}
@Test
@ResourceLock(Resources.SYSTEM_OUT)
void testWithSystemOut() {
PrintStream originalOut = System.out;
ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(capturedOut));
System.out.println("Test output");
assertEquals("Test output\n", capturedOut.toString());
System.setOut(originalOut);
}
@Test
@ResourceLock(Resources.LOCALE)
void testWithLocale() {
Locale original = Locale.getDefault();
Locale.setDefault(Locale.FRENCH);
// Test with French locale
assertEquals(Locale.FRENCH, Locale.getDefault());
Locale.setDefault(original);
}
}
```
### Custom Resource Locks Provider
Programmatically provide resource locks based on test context.
```java { .api }
/**
* Provides resource locks programmatically
*/
interface ResourceLocksProvider {
/**
* Provide resource locks for the given extension context
*/
Set<Lock> provideForClass(ExtensionContext context);
Set<Lock> provideForNestedClass(ExtensionContext context);
Set<Lock> provideForMethod(ExtensionContext context);
/**
* Resource lock representation
*/
interface Lock {
String getKey();
ResourceAccessMode getAccessMode();
}
}
```
### Temporary Directory Support
Automatic temporary directory creation and cleanup for tests.
```java { .api }
/**
* Inject temporary directory into test method or field
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface TempDir {
/**
* Cleanup mode for temporary directory
*/
CleanupMode cleanup() default CleanupMode.DEFAULT;
/**
* Factory for creating temporary directories
*/
Class<? extends TempDirFactory> factory() default TempDirFactory.Standard.class;
}
/**
* Cleanup mode for temporary directories
*/
enum CleanupMode {
/**
* Use default cleanup behavior
*/
DEFAULT,
/**
* Never clean up temporary directories
*/
NEVER,
/**
* Always clean up temporary directories
*/
ALWAYS,
/**
* Clean up on success, keep on failure
*/
ON_SUCCESS
}
/**
* Factory for creating temporary directories
*/
interface TempDirFactory {
/**
* Create temporary directory
*/
Path createTempDirectory(AnnotatedElement annotatedElement, ExtensionContext extensionContext) throws IOException;
/**
* Standard temporary directory factory
*/
class Standard implements TempDirFactory {
@Override
public Path createTempDirectory(AnnotatedElement annotatedElement, ExtensionContext extensionContext) throws IOException {
return Files.createTempDirectory("junit");
}
}
}
```
**Usage Examples:**
```java
class TempDirTest {
@TempDir
Path sharedTempDir;
@Test
void testWithSharedTempDir() throws IOException {
Path file = sharedTempDir.resolve("test.txt");
Files.write(file, "test content".getBytes());
assertTrue(Files.exists(file));
assertEquals("test content", Files.readString(file));
}
@Test
void testWithMethodTempDir(@TempDir Path tempDir) throws IOException {
// Each test method gets its own temp directory
assertNotEquals(sharedTempDir, tempDir);
Path file = tempDir.resolve("method-test.txt");
Files.createFile(file);
assertTrue(Files.exists(file));
}
@Test
void testWithCustomCleanup(@TempDir(cleanup = CleanupMode.NEVER) Path persistentDir) throws IOException {
// This directory won't be cleaned up automatically
Path file = persistentDir.resolve("persistent.txt");
Files.write(file, "This file will persist".getBytes());
System.out.println("Persistent dir: " + persistentDir);
}
@Test
void testWithCustomFactory(@TempDir(factory = CustomTempDirFactory.class) Path customDir) {
// Directory created by custom factory
assertTrue(customDir.toString().contains("custom"));
}
}
class CustomTempDirFactory implements TempDirFactory {
@Override
public Path createTempDirectory(AnnotatedElement annotatedElement, ExtensionContext extensionContext) throws IOException {
return Files.createTempDirectory("custom-junit-" + extensionContext.getDisplayName());
}
}
```
### Configuration Properties
Configure parallel execution behavior through system properties or configuration files.
**Key Configuration Properties:**
```properties
# Enable parallel execution
junit.jupiter.execution.parallel.enabled=true
# Default execution mode
junit.jupiter.execution.parallel.mode.default=concurrent
# Class-level execution mode
junit.jupiter.execution.parallel.mode.classes.default=concurrent
# Parallelism strategy
junit.jupiter.execution.parallel.config.strategy=dynamic
# or fixed with custom thread count
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=4
# Dynamic parallelism factor
junit.jupiter.execution.parallel.config.dynamic.factor=2.0
```
**Usage in junit-platform.properties:**
```properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=same_thread
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.config.dynamic.factor=1.5
```

View file

@ -0,0 +1,902 @@
# Parameterized Tests
Advanced parameterized testing capabilities that allow running the same test logic with different sets of arguments. JUnit Jupiter provides multiple ways to supply test arguments with support for custom conversion and aggregation.
## Imports
```java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import org.junit.jupiter.params.aggregator.*;
import org.junit.jupiter.params.converter.*;
import static org.junit.jupiter.api.Assertions.*;
```
## Capabilities
### Parameterized Test Annotation
Core annotation for defining parameterized tests.
```java { .api }
/**
* Marks a method as a parameterized test with multiple argument sources
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ParameterizedTest {
/**
* Custom name pattern for parameterized test invocations
*/
String name() default "[{index}] {arguments}";
/**
* How to handle argument count mismatches
*/
ArgumentCountValidationMode argumentCountValidationMode() default ArgumentCountValidationMode.STRICT;
}
enum ArgumentCountValidationMode {
STRICT, // Fail if parameter count doesn't match
LENIENT, // Allow missing parameters (null values)
IGNORE // Ignore extra arguments
}
```
**Basic Usage:**
```java
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testWithValueSource(int argument) {
assertTrue(argument > 0);
}
@ParameterizedTest(name = "Run {index}: testing with value {0}")
@ValueSource(strings = {"apple", "banana", "cherry"})
void testWithCustomName(String fruit) {
assertNotNull(fruit);
assertTrue(fruit.length() > 3);
}
```
### Value Sources
Simple argument sources for primitive types and strings.
```java { .api }
/**
* Array of literal values as arguments
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ValueArgumentsProvider.class)
@interface ValueSource {
short[] shorts() default {};
byte[] bytes() default {};
int[] ints() default {};
long[] longs() default {};
float[] floats() default {};
double[] doubles() default {};
char[] chars() default {};
boolean[] booleans() default {};
String[] strings() default {};
Class<?>[] classes() default {};
}
/**
* Container for multiple value sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValueSources {
ValueSource[] value();
}
```
**Usage Examples:**
```java
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testNumbers(int number) {
assertTrue(number > 0 && number < 6);
}
@ParameterizedTest
@ValueSource(strings = {"", " "})
void testBlankStrings(String input) {
assertTrue(input.isBlank());
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void testBooleans(boolean value) {
// Test both true and false cases
assertNotNull(Boolean.valueOf(value));
}
@ParameterizedTest
@ValueSource(classes = {String.class, Integer.class, List.class})
void testClasses(Class<?> clazz) {
assertNotNull(clazz);
assertNotNull(clazz.getName());
}
```
### Null and Empty Sources
Special argument sources for null and empty values.
```java { .api }
/**
* Provides a single null argument
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(NullArgumentsProvider.class)
@interface NullSource {
}
/**
* Provides empty values for strings, lists, sets, maps, and primitive arrays
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(EmptyArgumentsProvider.class)
@interface EmptySource {
}
/**
* Combines null and empty sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@NullSource
@EmptySource
@interface NullAndEmptySource {
}
```
**Usage Examples:**
```java
@ParameterizedTest
@NullSource
@ValueSource(strings = {"", " ", "valid"})
void testStringValidation(String input) {
// Test with null, empty, blank, and valid strings
String result = StringUtils.clean(input);
// Assert based on input type
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"apple", "banana"})
void testStringProcessing(String input) {
// Test null, empty, and actual values
String processed = processString(input);
if (input == null || input.isEmpty()) {
assertEquals("default", processed);
} else {
assertNotEquals("default", processed);
}
}
@ParameterizedTest
@EmptySource
@ValueSource(ints = {1, 2, 3})
void testIntArrays(int[] array) {
// Test with empty array and arrays with values
assertNotNull(array);
}
```
### Enum Sources
Arguments from enum values with filtering options.
```java { .api }
/**
* Provides enum values as arguments
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(EnumArgumentsProvider.class)
@interface EnumSource {
/**
* Enum class to get values from
*/
Class<? extends Enum<?>> value();
/**
* Enum constant names to include/exclude
*/
String[] names() default {};
/**
* Whether to include or exclude specified names
*/
Mode mode() default Mode.INCLUDE;
enum Mode {
INCLUDE, // Include only specified names
EXCLUDE, // Exclude specified names
MATCH_ALL, // Include names matching all patterns
MATCH_ANY // Include names matching any pattern
}
}
/**
* Container for multiple enum sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface EnumSources {
EnumSource[] value();
}
```
**Usage Examples:**
```java
enum Color {
RED, GREEN, BLUE, YELLOW, PURPLE
}
@ParameterizedTest
@EnumSource(Color.class)
void testAllColors(Color color) {
assertNotNull(color);
assertTrue(color.name().length() > 2);
}
@ParameterizedTest
@EnumSource(value = Color.class, names = {"RED", "BLUE"})
void testSpecificColors(Color color) {
assertTrue(color == Color.RED || color == Color.BLUE);
}
@ParameterizedTest
@EnumSource(value = Color.class, names = {"YELLOW"}, mode = EnumSource.Mode.EXCLUDE)
void testAllColorsExceptYellow(Color color) {
assertNotEquals(Color.YELLOW, color);
}
@ParameterizedTest
@EnumSource(value = Color.class, names = {"^B.*"}, mode = EnumSource.Mode.MATCH_ALL)
void testColorsStartingWithB(Color color) {
assertTrue(color.name().startsWith("B"));
}
```
### CSV Sources
Arguments from CSV data, either inline or from files.
```java { .api }
/**
* Provides CSV data as arguments
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(CsvArgumentsProvider.class)
@interface CsvSource {
/**
* CSV records as string array
*/
String[] value();
/**
* Column delimiter character
*/
char delimiter() default ',';
/**
* String to represent null values
*/
String nullValues() default "";
/**
* Quote character for escaping
*/
char quoteCharacter() default '"';
/**
* How to handle empty values
*/
EmptyValue emptyValue() default EmptyValue.EMPTY_STRING;
/**
* Whether to ignore leading/trailing whitespace
*/
boolean ignoreLeadingAndTrailingWhitespace() default true;
enum EmptyValue {
EMPTY_STRING, // Empty string ""
NULL_REFERENCE // null
}
}
/**
* Container for multiple CSV sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface CsvSources {
CsvSource[] value();
}
```
**Usage Examples:**
```java
@ParameterizedTest
@CsvSource({
"apple, 1",
"banana, 2",
"'lemon, lime', 3"
})
void testWithCsvSource(String fruit, int rank) {
assertNotNull(fruit);
assertTrue(rank > 0);
}
@ParameterizedTest
@CsvSource(value = {
"John:25:Engineer",
"Jane:30:Manager",
"Bob:35:Developer"
}, delimiter = ':')
void testPersonData(String name, int age, String role) {
assertNotNull(name);
assertTrue(age > 0);
assertNotNull(role);
}
@ParameterizedTest
@CsvSource(value = {
"test, NULL, 42",
"example, , 0"
}, nullValues = "NULL")
void testWithNullValues(String str, String nullableStr, int number) {
assertNotNull(str);
// nullableStr might be null
assertTrue(number >= 0);
}
```
### CSV File Sources
Arguments from external CSV files.
```java { .api }
/**
* Provides CSV data from files as arguments
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(CsvFileArgumentsProvider.class)
@interface CsvFileSource {
/**
* CSV file resources (classpath relative)
*/
String[] resources() default {};
/**
* CSV files (file system paths)
*/
String[] files() default {};
/**
* Character encoding for files
*/
String encoding() default "UTF-8";
/**
* Line separator for files
*/
String lineSeparator() default "\n";
/**
* Column delimiter character
*/
char delimiter() default ',';
/**
* String to represent null values
*/
String nullValues() default "";
/**
* Quote character for escaping
*/
char quoteCharacter() default '"';
/**
* How to handle empty values
*/
CsvSource.EmptyValue emptyValue() default CsvSource.EmptyValue.EMPTY_STRING;
/**
* Whether to ignore leading/trailing whitespace
*/
boolean ignoreLeadingAndTrailingWhitespace() default true;
/**
* Number of header lines to skip
*/
int numLinesToSkip() default 0;
}
/**
* Container for multiple CSV file sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface CsvFileSources {
CsvFileSource[] value();
}
```
**Usage Examples:**
```java
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv", numLinesToSkip = 1)
void testWithCsvFileSource(String name, int age, String city) {
assertNotNull(name);
assertTrue(age > 0);
assertNotNull(city);
}
@ParameterizedTest
@CsvFileSource(files = "src/test/resources/users.csv", delimiter = ';')
void testUserData(String username, String email, boolean active) {
assertNotNull(username);
assertTrue(email.contains("@"));
// active can be true or false
}
```
### Method Sources
Arguments from static methods.
```java { .api }
/**
* Provides arguments from static methods
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(MethodArgumentsProvider.class)
@interface MethodSource {
/**
* Method names that provide arguments
* If empty, uses test method name
*/
String[] value() default {};
}
/**
* Container for multiple method sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MethodSources {
MethodSource[] value();
}
```
**Usage Examples:**
```java
@ParameterizedTest
@MethodSource("stringProvider")
void testWithMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> stringProvider() {
return Stream.of("apple", "banana", "cherry");
}
@ParameterizedTest
@MethodSource("personProvider")
void testPersons(Person person) {
assertNotNull(person.getName());
assertTrue(person.getAge() > 0);
}
static Stream<Person> personProvider() {
return Stream.of(
new Person("John", 25),
new Person("Jane", 30),
new Person("Bob", 35)
);
}
@ParameterizedTest
@MethodSource("argumentProvider")
void testWithMultipleArguments(int number, String text, boolean flag) {
assertTrue(number > 0);
assertNotNull(text);
// flag can be any boolean value
}
static Stream<Arguments> argumentProvider() {
return Stream.of(
Arguments.of(1, "first", true),
Arguments.of(2, "second", false),
Arguments.of(3, "third", true)
);
}
```
### Field Sources
Arguments from static fields.
```java { .api }
/**
* Provides arguments from static fields
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(FieldArgumentsProvider.class)
@interface FieldSource {
/**
* Field names that provide arguments
* If empty, uses test method name
*/
String[] value() default {};
}
/**
* Container for multiple field sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface FieldSources {
FieldSource[] value();
}
```
**Usage Examples:**
```java
static List<String> fruits = Arrays.asList("apple", "banana", "cherry");
@ParameterizedTest
@FieldSource("fruits")
void testWithFieldSource(String fruit) {
assertNotNull(fruit);
assertTrue(fruit.length() > 3);
}
static Stream<Arguments> testData = Stream.of(
Arguments.of(1, "one"),
Arguments.of(2, "two"),
Arguments.of(3, "three")
);
@ParameterizedTest
@FieldSource("testData")
void testWithArgumentsField(int number, String word) {
assertTrue(number > 0);
assertNotNull(word);
}
```
### Custom Argument Sources
Create custom argument providers for complex scenarios.
```java { .api }
/**
* Custom arguments source annotation
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(CustomArgumentsProvider.class)
@interface ArgumentsSource {
/**
* ArgumentsProvider implementation class
*/
Class<? extends ArgumentsProvider> value();
}
/**
* Container for multiple custom sources
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface ArgumentsSources {
ArgumentsSource[] value();
}
/**
* Arguments provider interface
*/
interface ArgumentsProvider {
/**
* Provide arguments for parameterized test
*/
Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception;
}
/**
* Base class for annotation-based providers
*/
abstract class AnnotationBasedArgumentsProvider<T extends Annotation> implements ArgumentsProvider {
/**
* Accept annotation for configuration
*/
protected abstract void accept(T annotation);
}
```
**Usage Example:**
```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(RandomIntegerProvider.class)
@interface RandomIntegers {
int count() default 10;
int min() default 0;
int max() default 100;
}
class RandomIntegerProvider extends AnnotationBasedArgumentsProvider<RandomIntegers> {
private int count;
private int min;
private int max;
@Override
protected void accept(RandomIntegers annotation) {
this.count = annotation.count();
this.min = annotation.min();
this.max = annotation.max();
}
@Override
public Stream<Arguments> provideArguments(ExtensionContext context) {
Random random = new Random();
return random.ints(count, min, max)
.mapToObj(Arguments::of);
}
}
@ParameterizedTest
@RandomIntegers(count = 5, min = 1, max = 10)
void testWithRandomIntegers(int value) {
assertTrue(value >= 1 && value <= 10);
}
```
### Argument Conversion
Convert string arguments to other types automatically or with custom converters.
```java { .api }
/**
* Custom argument converter annotation
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertWith {
/**
* ArgumentConverter implementation class
*/
Class<? extends ArgumentConverter> value();
}
/**
* Java time conversion pattern
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@ConvertWith(JavaTimeArgumentConverter.class)
@interface JavaTimeConversionPattern {
/**
* Pattern for parsing date/time
*/
String value();
}
/**
* Argument converter interface
*/
interface ArgumentConverter<S, T> {
/**
* Convert source argument to target type
*/
T convert(S source, ParameterContext context) throws ArgumentConversionException;
}
/**
* Simple converter for single argument types
*/
abstract class SimpleArgumentConverter<S, T> implements ArgumentConverter<S, T> {
@Override
public final T convert(S source, ParameterContext context) throws ArgumentConversionException {
return convert(source, context.getParameter().getType());
}
/**
* Convert source to target type
*/
protected abstract T convert(S source, Class<?> targetType) throws ArgumentConversionException;
}
/**
* Typed converter with type safety
*/
abstract class TypedArgumentConverter<S, T> extends SimpleArgumentConverter<S, T> {
private final Class<S> sourceType;
private final Class<T> targetType;
protected TypedArgumentConverter(Class<S> sourceType, Class<T> targetType) {
this.sourceType = sourceType;
this.targetType = targetType;
}
}
```
**Usage Examples:**
```java
@ParameterizedTest
@ValueSource(strings = {"2023-01-01", "2023-12-31"})
void testDates(@JavaTimeConversionPattern("yyyy-MM-dd") LocalDate date) {
assertNotNull(date);
assertEquals(2023, date.getYear());
}
class StringToPersonConverter extends TypedArgumentConverter<String, Person> {
protected StringToPersonConverter() {
super(String.class, Person.class);
}
@Override
protected Person convert(String source, Class<?> targetType) {
String[] parts = source.split(",");
return new Person(parts[0], Integer.parseInt(parts[1]));
}
}
@ParameterizedTest
@ValueSource(strings = {"John,25", "Jane,30", "Bob,35"})
void testPersonConversion(@ConvertWith(StringToPersonConverter.class) Person person) {
assertNotNull(person.getName());
assertTrue(person.getAge() > 0);
}
```
### Argument Aggregation
Aggregate multiple arguments into complex objects.
```java { .api }
/**
* Custom argument aggregator annotation
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@interface AggregateWith {
/**
* ArgumentsAggregator implementation class
*/
Class<? extends ArgumentsAggregator> value();
}
/**
* Arguments aggregator interface
*/
interface ArgumentsAggregator {
/**
* Aggregate arguments into single object
*/
Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context)
throws ArgumentsAggregationException;
}
/**
* Arguments accessor for retrieving individual arguments
*/
interface ArgumentsAccessor {
Object get(int index);
<T> T get(int index, Class<T> requiredType);
Character getCharacter(int index);
Boolean getBoolean(int index);
Byte getByte(int index);
Short getShort(int index);
Integer getInteger(int index);
Long getLong(int index);
Float getFloat(int index);
Double getDouble(int index);
String getString(int index);
int size();
Object[] toArray();
List<Object> toList();
}
```
**Usage Examples:**
```java
class PersonAggregator implements ArgumentsAggregator {
@Override
public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) {
return new Person(accessor.getString(0), accessor.getInteger(1));
}
}
@ParameterizedTest
@CsvSource({
"John, 25",
"Jane, 30",
"Bob, 35"
})
void testPersonAggregation(@AggregateWith(PersonAggregator.class) Person person) {
assertNotNull(person.getName());
assertTrue(person.getAge() > 0);
}
@ParameterizedTest
@CsvSource({
"John, 25, Engineer",
"Jane, 30, Manager",
"Bob, 35, Developer"
})
void testWithArgumentsAccessor(ArgumentsAccessor arguments) {
String name = arguments.getString(0);
int age = arguments.getInteger(1);
String role = arguments.getString(2);
Person person = new Person(name, age, role);
assertNotNull(person);
}
```
### Arguments Utility
Utility class for creating argument sets programmatically.
```java { .api }
/**
* Factory for creating Arguments instances
*/
interface Arguments {
/**
* Create Arguments from array of objects
*/
static Arguments of(Object... arguments);
/**
* Get arguments as object array
*/
Object[] get();
}
```
**Usage Example:**
```java
static Stream<Arguments> complexArgumentProvider() {
return Stream.of(
Arguments.of(1, "apple", true, new Person("John", 25)),
Arguments.of(2, "banana", false, new Person("Jane", 30)),
Arguments.of(3, "cherry", true, new Person("Bob", 35))
);
}
@ParameterizedTest
@MethodSource("complexArgumentProvider")
void testComplexArguments(int id, String fruit, boolean active, Person person) {
assertTrue(id > 0);
assertNotNull(fruit);
assertNotNull(person);
}
```

View file

@ -0,0 +1,7 @@
{
"name": "tessl/maven-org-junit-jupiter--junit-jupiter",
"version": "5.12.0",
"docs": "docs/index.md",
"describes": "pkg:maven/org.junit.jupiter/junit-jupiter@5.12.2",
"summary": "JUnit Jupiter aggregator module providing a unified API for JUnit 5 testing framework with core API, parameterized tests, and test engine."
}

View file

@ -0,0 +1,486 @@
# Configuration Management
Babel configuration loading, validation, and management system supporting various config file formats, runtime options, and plugin/preset resolution. Provides both full and partial configuration loading for different use cases.
## Capabilities
### Options Loading
Load and resolve complete Babel configuration from various sources.
```typescript { .api }
/**
* Load complete Babel options synchronously
* @param opts - Input options to merge with config file settings
* @returns Resolved configuration object or null if no config found
*/
function loadOptionsSync(opts?: InputOptions): ResolvedConfig | null;
/**
* Load complete Babel options asynchronously
* @param opts - Input options to merge with config file settings
* @returns Promise resolving to resolved configuration or null
*/
function loadOptionsAsync(opts?: InputOptions): Promise<ResolvedConfig | null>;
/**
* Load complete Babel options with callback (legacy API, deprecated in Babel 8)
* @param opts - Input options to merge with config file settings
* @param callback - Callback function receiving error and resolved config
*/
function loadOptions(
opts: InputOptions,
callback: (err: Error | null, config: ResolvedConfig | null) => void
): void;
function loadOptions(
callback: (err: Error | null, config: ResolvedConfig | null) => void
): void;
interface ResolvedConfig {
/** Resolved plugins with their options */
plugins: Array<ConfigItem>;
/** Resolved presets with their options */
presets: Array<ConfigItem>;
/** Parser options */
parserOpts: ParserOptions;
/** Generator options */
generatorOpts: GeneratorOptions;
/** All other resolved options */
[key: string]: any;
}
```
**Usage Examples:**
```typescript
import { loadOptionsSync, loadOptionsAsync } from "@babel/core";
// Load configuration from babel.config.js and .babelrc files
const config = loadOptionsSync({
cwd: "/path/to/project",
filename: "src/app.js",
envName: "production"
});
if (config) {
console.log("Plugins:", config.plugins.map(p => p.name));
console.log("Presets:", config.presets.map(p => p.name));
console.log("Parser options:", config.parserOpts);
}
// Override config file settings
const customConfig = loadOptionsSync({
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-runtime"],
targets: "> 0.25%, not dead"
});
// Async loading
const asyncConfig = await loadOptionsAsync({
cwd: process.cwd(),
configFile: "./babel.config.json",
envName: process.env.NODE_ENV
});
```
### Partial Configuration
Load partial configuration for advanced use cases where full resolution isn't needed.
```typescript { .api }
/**
* Load partial Babel configuration synchronously
* @param opts - Input options for partial resolution
* @returns Partial configuration object or null
*/
function loadPartialConfigSync(opts?: InputOptions): PartialConfig | null;
/**
* Load partial Babel configuration asynchronously
* @param opts - Input options for partial resolution
* @returns Promise resolving to partial configuration or null
*/
function loadPartialConfigAsync(opts?: InputOptions): Promise<PartialConfig | null>;
/**
* Load partial configuration with callback (legacy API, deprecated in Babel 8)
* @param opts - Input options for partial resolution
* @param callback - Callback function receiving error and partial config
*/
function loadPartialConfig(
opts: InputOptions,
callback: (err: Error | null, config: PartialConfig | null) => void
): void;
function loadPartialConfig(
callback: (err: Error | null, config: PartialConfig | null) => void
): void;
interface PartialConfig {
/** Resolved options (may be null if no valid config) */
options: ResolvedConfig | null;
/** Loaded config file information */
config?: {
filepath: string;
dirname: string;
options: any;
};
/** Loaded .babelrc file information */
babelrc?: {
filepath: string;
dirname: string;
options: any;
};
/** Whether this config ignores the file */
hasFilesystemConfig(): boolean;
}
```
**Usage Examples:**
```typescript
import { loadPartialConfigSync } from "@babel/core";
// Check if a file should be processed
const partialConfig = loadPartialConfigSync({
filename: "src/components/Button.tsx",
cwd: "/path/to/project"
});
if (partialConfig) {
if (partialConfig.hasFilesystemConfig()) {
console.log("File has Babel config");
if (partialConfig.config) {
console.log("Config file:", partialConfig.config.filepath);
}
if (partialConfig.babelrc) {
console.log("Babelrc file:", partialConfig.babelrc.filepath);
}
// Use resolved options
if (partialConfig.options) {
console.log("Resolved plugins:", partialConfig.options.plugins.length);
}
} else {
console.log("No Babel config found for this file");
}
}
```
### Configuration Items
Create and manage individual plugin and preset configuration items.
```typescript { .api }
/**
* Create configuration item synchronously
* @param target - Plugin/preset function, module name, or path
* @param options - Options to pass to the plugin/preset
* @returns Configuration item or null if invalid
*/
function createConfigItemSync(
target: PluginTarget,
options?: ConfigItemOptions
): ConfigItem<PluginAPI> | null;
/**
* Create configuration item asynchronously
* @param target - Plugin/preset function, module name, or path
* @param options - Options to pass to the plugin/preset
* @returns Promise resolving to configuration item or null
*/
function createConfigItemAsync(
target: PluginTarget,
options?: ConfigItemOptions
): Promise<ConfigItem<PluginAPI> | null>;
/**
* Create configuration item with callback (legacy API, deprecated in Babel 8)
* @param target - Plugin/preset function, module name, or path
* @param options - Options to pass to the plugin/preset
* @param callback - Callback function receiving error and config item
*/
function createConfigItem(
target: PluginTarget,
options: ConfigItemOptions,
callback: (err: Error | null, item: ConfigItem<PluginAPI> | null) => void
): void;
type PluginTarget =
| string
| PluginFunction
| PresetFunction
| [string, any]
| [PluginFunction, any]
| [PresetFunction, any];
interface ConfigItemOptions {
/** Directory context for resolution */
dirname?: string;
/** Item type: "plugin" or "preset" */
type?: "plugin" | "preset";
}
interface ConfigItem<T = PluginAPI> {
/** Resolved plugin/preset function */
value: T;
/** Configuration options passed to the plugin/preset */
options: any;
/** Directory where the plugin/preset was resolved */
dirname: string;
/** Name of the plugin/preset */
name?: string;
/** Full file path if resolved from file */
file?: {
request: string;
resolved: string;
};
}
```
**Usage Examples:**
```typescript
import { createConfigItemSync } from "@babel/core";
// Create plugin config item
const pluginItem = createConfigItemSync("@babel/plugin-transform-runtime", {
dirname: "/path/to/project",
type: "plugin"
});
if (pluginItem) {
console.log("Plugin name:", pluginItem.name);
console.log("Plugin options:", pluginItem.options);
console.log("Resolved from:", pluginItem.file?.resolved);
}
// Create preset config item with options
const presetItem = createConfigItemSync(
["@babel/preset-env", {
targets: "> 0.25%, not dead",
useBuiltIns: "usage",
corejs: 3
}],
{
dirname: process.cwd(),
type: "preset"
}
);
// Create from function
const customPlugin = function(babel) {
return {
visitor: {
Identifier(path) {
console.log("Found identifier:", path.node.name);
}
}
};
};
const customItem = createConfigItemSync(customPlugin, {
dirname: __dirname,
type: "plugin"
});
```
## Configuration File Support
Babel supports various configuration file formats:
```typescript { .api }
interface ConfigFileOptions {
/** Path to specific config file, or false to disable */
configFile?: string | false;
/** Enable/disable .babelrc file loading */
babelrc?: boolean;
/** Root directory for config file search */
root?: string;
/** Current working directory */
cwd?: string;
/** Override root mode: "root", "upward", or "upward-optional" */
rootMode?: "root" | "upward" | "upward-optional";
}
```
**Supported Config Files:**
- `babel.config.json` - Project-wide configuration
- `babel.config.js` - Project-wide with JavaScript
- `babel.config.mjs` - Project-wide with ES modules
- `babel.config.cjs` - Project-wide with CommonJS
- `.babelrc` - File-relative configuration
- `.babelrc.json` - File-relative JSON
- `.babelrc.js` - File-relative JavaScript
- `package.json` - Babel field in package.json
**Usage Examples:**
```typescript
import { loadOptionsSync } from "@babel/core";
// Use specific config file
const config1 = loadOptionsSync({
configFile: "./babel.production.js",
cwd: "/path/to/project"
});
// Disable config file loading
const config2 = loadOptionsSync({
configFile: false,
plugins: ["@babel/plugin-transform-arrow-functions"]
});
// Disable .babelrc files
const config3 = loadOptionsSync({
babelrc: false,
presets: ["@babel/preset-env"]
});
// Search from different root
const config4 = loadOptionsSync({
root: "/different/root",
rootMode: "upward",
filename: "src/app.js"
});
```
## Environment-based Configuration
Configure Babel behavior based on environment:
```typescript { .api }
interface EnvironmentOptions {
/** Environment name (defaults to BABEL_ENV || NODE_ENV || "development") */
envName?: string;
/** Caller metadata for conditional configuration */
caller?: CallerMetadata;
}
interface CallerMetadata {
/** Name of the calling tool */
name: string;
/** Version of the calling tool */
version?: string;
/** Whether the caller supports ES modules */
supportsStaticESM?: boolean;
/** Whether the caller supports dynamic imports */
supportsDynamicImport?: boolean;
/** Whether the caller supports top-level await */
supportsTopLevelAwait?: boolean;
/** Additional caller-specific properties */
[key: string]: any;
}
```
**Usage Examples:**
```typescript
import { loadOptionsSync } from "@babel/core";
// Load development configuration
const devConfig = loadOptionsSync({
envName: "development",
caller: {
name: "webpack",
version: "5.0.0",
supportsStaticESM: true
}
});
// Load production configuration
const prodConfig = loadOptionsSync({
envName: "production",
caller: {
name: "rollup",
version: "2.0.0",
supportsDynamicImport: true
}
});
// Override environment
process.env.NODE_ENV = "test";
const testConfig = loadOptionsSync({
envName: "testing", // Overrides NODE_ENV
filename: "test/example.spec.js"
});
```
## Advanced Configuration Patterns
### Conditional Configuration
```javascript
// babel.config.js
module.exports = function(api) {
// Cache configuration based on environment
api.cache.using(() => process.env.NODE_ENV);
const presets = ["@babel/preset-env"];
const plugins = [];
// Add plugins based on environment
if (api.env("development")) {
plugins.push("react-refresh/babel");
}
// Add plugins based on caller
if (api.caller(caller => caller?.name === "webpack")) {
plugins.push("@babel/plugin-syntax-dynamic-import");
}
return { presets, plugins };
};
```
### Programmatic Configuration
```typescript
import { loadOptionsSync, transformSync } from "@babel/core";
// Build configuration programmatically
const baseConfig = loadOptionsSync({
presets: ["@babel/preset-env"],
configFile: false
});
// Extend with additional plugins
const extendedConfig = {
...baseConfig,
plugins: [
...baseConfig.plugins,
["@babel/plugin-transform-runtime", { corejs: 3 }]
]
};
// Use extended configuration
const result = transformSync(code, extendedConfig);
```
## Error Handling
Configuration functions may throw errors for invalid configurations:
```typescript
import { loadOptionsSync, createConfigItemSync } from "@babel/core";
try {
const config = loadOptionsSync({
plugins: ["non-existent-plugin"]
});
} catch (error) {
if (error.code === "BABEL_UNKNOWN_PLUGIN") {
console.error("Unknown plugin:", error.message);
}
}
try {
const item = createConfigItemSync("invalid-plugin", {
dirname: "/nonexistent"
});
} catch (error) {
console.error("Failed to create config item:", error.message);
}
```

View file

@ -0,0 +1,398 @@
# Babel Core
Babel Core is the core compiler for Babel, providing programmatic APIs for JavaScript code transformation, parsing, and configuration. It enables developers to transpile modern JavaScript code into backward-compatible versions, parse JavaScript into Abstract Syntax Trees (ASTs), and configure the transformation process through plugins and presets.
## Package Information
- **Package Name**: @babel/core
- **Package Type**: npm
- **Language**: TypeScript
- **Installation**: `npm install @babel/core`
## Core Imports
```typescript
import * as babel from "@babel/core";
```
For specific functions:
```typescript
import {
transform,
transformSync,
parse,
parseSync,
loadOptions,
createConfigItem,
types,
traverse,
template,
type PluginPass,
type Visitor,
type NodePath,
type Scope
} from "@babel/core";
```
CommonJS:
```javascript
const babel = require("@babel/core");
const { transform, parse, loadOptions } = require("@babel/core");
```
## Basic Usage
```typescript
import { transformSync, parseSync } from "@babel/core";
// Transform JavaScript code
const result = transformSync(`
const arrow = () => console.log("Hello");
class MyClass {
method() { return 42; }
}
`, {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-arrow-functions"]
});
console.log(result.code);
// Output: Transpiled ES5 compatible code
// Parse JavaScript to AST
const ast = parseSync(`
function hello() {
return "world";
}
`, {
sourceType: "module",
plugins: ["jsx", "typescript"]
});
console.log(ast.type); // "File"
```
## Architecture
Babel Core is built around several key components:
- **Transformation Engine**: Core APIs (`transform`, `transformSync`, `transformAsync`) that apply plugins and presets to JavaScript code
- **Parser Interface**: Wrapper around @babel/parser (`parse`, `parseSync`, `parseAsync`) for AST generation
- **Configuration System**: Option loading and validation (`loadOptions`, `loadPartialConfig`) with support for config files
- **Plugin/Preset Management**: Configuration item creation and resolution (`createConfigItem`, `resolvePlugin`, `resolvePreset`)
- **File Processing**: File-based transformation APIs (`transformFile`, `transformFileSync`, `transformFileAsync`)
- **AST Processing**: Direct AST transformation (`transformFromAst`, `transformFromAstSync`, `transformFromAstAsync`)
## Browser Compatibility
Babel Core includes browser-compatible variants for client-side usage:
- **File system operations** are replaced with browser-compatible alternatives
- **Config file resolution** is modified for browser environments
- **Transform file APIs** (`transformFile*`) use alternative implementations that don't rely on Node.js file system
- **Module resolution** adapts to browser module loading patterns
The package automatically uses browser-compatible versions when bundled for web environments through the `browser` field in package.json.
## Capabilities
### Code Transformation
Core JavaScript transformation functionality supporting both code strings and files, with synchronous and asynchronous variants.
```typescript { .api }
function transformSync(
code: string,
opts?: InputOptions
): FileResult | null;
function transformAsync(
code: string,
opts?: InputOptions
): Promise<FileResult | null>;
function transform(code: string, callback: FileResultCallback): void;
function transform(
code: string,
opts: InputOptions | null | undefined,
callback: FileResultCallback
): void;
type FileResultCallback = (err: Error | null, result: FileResult | null) => void;
interface FileResult {
code: string | null;
map: object | null;
ast: object | null;
metadata: object;
}
```
[Transformation](./transformation.md)
### Code Parsing
JavaScript parsing functionality that converts source code into Abstract Syntax Trees (ASTs) using Babel's parser.
```typescript { .api }
function parseSync(
code: string,
opts?: InputOptions
): ParseResult | null;
function parseAsync(
code: string,
opts?: InputOptions
): Promise<ParseResult | null>;
function parse(code: string, callback: FileParseCallback): void;
function parse(
code: string,
opts: InputOptions | null | undefined,
callback: FileParseCallback
): void;
type ParseResult = import("@babel/types").File;
type FileParseCallback = (err: Error | null, ast: ParseResult | null) => void;
```
[Parsing](./parsing.md)
### Configuration Management
Babel configuration loading, validation, and management system supporting various config file formats and runtime options.
```typescript { .api }
function loadOptionsSync(opts?: InputOptions): ResolvedConfig | null;
function loadOptionsAsync(opts?: InputOptions): Promise<ResolvedConfig | null>;
function loadOptions(opts: InputOptions, callback: (err: Error | null, config: ResolvedConfig | null) => void): void;
function loadOptions(callback: (err: Error | null, config: ResolvedConfig | null) => void): void;
function loadPartialConfigSync(opts?: InputOptions): PartialConfig | null;
function loadPartialConfigAsync(opts?: InputOptions): Promise<PartialConfig | null>;
function loadPartialConfig(opts: InputOptions, callback: (err: Error | null, config: PartialConfig | null) => void): void;
function loadPartialConfig(callback: (err: Error | null, config: PartialConfig | null) => void): void;
function createConfigItemSync(
target: PluginTarget,
options?: any
): ConfigItem<PluginAPI> | null;
function createConfigItemAsync(
target: PluginTarget,
options?: any
): Promise<ConfigItem<PluginAPI> | null>;
function createConfigItem(
target: PluginTarget,
options: any,
callback: (err: Error | null, item: ConfigItem<PluginAPI> | null) => void
): void;
```
[Configuration](./configuration.md)
### Utilities and Constants
Helper functions, constants, and re-exported APIs from the Babel ecosystem.
```typescript { .api }
const version: string;
const DEFAULT_EXTENSIONS: readonly string[];
function getEnv(defaultValue?: string): string;
function resolvePlugin(name: string, dirname: string): string;
function resolvePreset(name: string, dirname: string): string;
```
[Utilities](./utilities.md)
## Core Types
```typescript { .api }
interface InputOptions {
/** Input source code filename for error reporting and source maps */
filename?: string;
/** Input source type: "script", "module", or "unambiguous" */
sourceType?: "script" | "module" | "unambiguous";
/** Array of plugins to apply during transformation */
plugins?: PluginItem[];
/** Array of presets to apply during transformation */
presets?: PresetItem[];
/** Parser options passed to @babel/parser */
parserOpts?: ParserOptions;
/** Generator options passed to @babel/generator */
generatorOpts?: GeneratorOptions;
/** Whether to include AST in result */
ast?: boolean;
/** Source map generation options */
sourceMaps?: boolean | "inline" | "both";
/** Code compaction options */
compact?: boolean | "auto";
/** Root directory for config file search */
root?: string;
/** Current working directory */
cwd?: string;
/** Environment name for conditional config */
envName?: string;
/** Babel configuration file path or search behavior */
configFile?: string | false;
/** .babelrc file search behavior */
babelrc?: boolean;
/** Metadata about the calling tool */
caller?: CallerMetadata;
}
interface CallerMetadata {
name: string;
version?: string;
[key: string]: any;
}
type PluginItem = string | [string, any] | PluginFunction | [PluginFunction, any];
type PresetItem = string | [string, any] | PresetFunction | [PresetFunction, any];
interface FileResult {
/** Transformed JavaScript code */
code: string | null;
/** Source map for the transformation */
map: object | null;
/** AST if requested via ast: true option */
ast: object | null;
/** Metadata from plugins and transformation process */
metadata: {
[key: string]: any;
};
}
interface ResolvedConfig {
/** Resolved and validated options */
[key: string]: any;
}
interface PartialConfig {
/** Partial configuration that may need further resolution */
options: ResolvedConfig | null;
config?: any;
babelrc?: any;
[key: string]: any;
}
interface ConfigItem<T = PluginAPI> {
/** Plugin or preset value */
value: T;
/** Configuration options */
options: any;
/** Directory context */
dirname: string;
/** Item name */
name?: string;
}
interface PluginAPI {
/** Plugin target metadata */
[key: string]: any;
}
interface PluginPass {
/** Current transformation file context */
file: File;
/** Plugin key/name */
key: string;
/** Plugin options */
opts: any;
/** Current working directory */
cwd: string;
/** Filename being processed */
filename?: string;
}
type Visitor<S = unknown> = {
/** Called when entering any AST node */
enter?(path: NodePath, state: S): void;
/** Called when exiting any AST node */
exit?(path: NodePath, state: S): void;
/** Specific node type visitors (e.g., FunctionDeclaration, Identifier) */
[NodeType: string]:
| ((path: NodePath, state: S) => void)
| { enter?(path: NodePath, state: S): void; exit?(path: NodePath, state: S): void }
| undefined;
};
interface NodePath<T = any> {
/** The AST node this path represents */
node: T;
/** Parent path */
parent: NodePath | null;
/** Parent AST node */
parentPath: NodePath | null;
/** Current scope information */
scope: Scope;
/** Current state passed through traversal */
state: any;
/** Array of child paths */
paths?: NodePath[];
/** Key in parent node */
key?: string | number;
/** Index if parent is array */
listKey?: string;
/** Replace this node with a new node */
replaceWith(node: any): void;
/** Remove this node */
remove(): void;
/** Skip traversing children of this node */
skip(): void;
/** Stop traversal entirely */
stop(): void;
/** Get the source code for this node */
getSource(): string;
/** Check if this path represents a specific node type */
isNodeType(type: string): boolean;
/** Find parent path of specific type */
findParent(callback: (path: NodePath) => boolean): NodePath | null;
/** Get binding information for identifier */
get(key: string): NodePath | NodePath[] | null;
}
interface Scope {
/** Parent scope */
parent: Scope | null;
/** Path that created this scope */
path: NodePath;
/** Block that created this scope */
block: any;
/** All bindings in this scope */
bindings: { [name: string]: Binding };
/** Referenced identifiers */
references: { [name: string]: any[] };
/** Global scope references */
globals: { [name: string]: any };
/** Check if identifier is bound in this scope */
hasBinding(name: string): boolean;
/** Get binding for identifier */
getBinding(name: string): Binding | undefined;
/** Generate unique identifier */
generateUid(name?: string): string;
/** Add binding to scope */
registerBinding(kind: string, path: NodePath): void;
}
interface Binding {
/** Identifier name */
identifier: any;
/** Scope this binding belongs to */
scope: Scope;
/** Path that created the binding */
path: NodePath;
/** Kind of binding (var, let, const, function, etc.) */
kind: string;
/** Whether binding is referenced */
referenced: boolean;
/** Number of references */
references: number;
/** All reference paths */
referencePaths: NodePath[];
}
```

View file

@ -0,0 +1,430 @@
# Code Parsing
JavaScript parsing functionality that converts source code into Abstract Syntax Trees (ASTs) using Babel's parser. Provides both synchronous and asynchronous parsing with extensive configuration options for different JavaScript dialects and syntax extensions.
## Capabilities
### String Parsing
Parse JavaScript code from strings into Babel ASTs.
```typescript { .api }
/**
* Parse JavaScript code synchronously
* @param code - JavaScript source code to parse
* @param opts - Parsing options including syntax plugins and parser settings
* @returns Babel AST (File node) or null if parsing fails
*/
function parseSync(code: string, opts?: InputOptions): ParseResult | null;
/**
* Parse JavaScript code asynchronously
* @param code - JavaScript source code to parse
* @param opts - Parsing options including syntax plugins and parser settings
* @returns Promise resolving to Babel AST (File node) or null
*/
function parseAsync(code: string, opts?: InputOptions): Promise<ParseResult | null>;
/**
* Parse JavaScript code with callback (legacy API, deprecated in Babel 8)
* @param code - JavaScript source code to parse
* @param opts - Parsing options
* @param callback - Callback function receiving error and AST result
*/
function parse(
code: string,
opts: InputOptions | null | undefined,
callback: FileParseCallback
): void;
function parse(code: string, callback: FileParseCallback): void;
type ParseResult = import("@babel/types").File;
type FileParseCallback = (err: Error | null, ast: ParseResult | null) => void;
```
**Usage Examples:**
```typescript
import { parseSync, parseAsync } from "@babel/core";
// Basic parsing
const ast = parseSync(`
function hello(name) {
return \`Hello \${name}!\`;
}
`, {
sourceType: "module"
});
console.log(ast.type); // "File"
console.log(ast.program.type); // "Program"
console.log(ast.program.body[0].type); // "FunctionDeclaration"
// Parsing with TypeScript syntax
const tsAst = parseSync(`
interface User {
name: string;
age: number;
}
const user: User = { name: "Alice", age: 30 };
`, {
sourceType: "module",
plugins: ["typescript"]
});
// Parsing with JSX syntax
const jsxAst = parseSync(`
const Component = () => {
return <div>Hello World</div>;
};
`, {
sourceType: "module",
plugins: ["jsx"]
});
// Asynchronous parsing
const asyncAst = await parseAsync(`
async function getData() {
const response = await fetch('/api/data');
return response.json();
}
`, {
sourceType: "module",
plugins: ["asyncGenerators"]
});
```
### Parser Configuration
Configure the parsing behavior with various options:
```typescript { .api }
interface ParseOptions {
/** Source type: "script", "module", or "unambiguous" (default: "script") */
sourceType?: "script" | "module" | "unambiguous";
/** Filename for error reporting and source maps */
filename?: string;
/** Parser-specific options */
parserOpts?: ParserOptions;
/** Environment name for conditional parsing */
envName?: string;
/** Current working directory */
cwd?: string;
/** Root directory for config resolution */
root?: string;
}
interface ParserOptions {
/** Syntax plugins to enable */
plugins?: ParserPlugin[];
/** Source type override */
sourceType?: "script" | "module" | "unambiguous";
/** Allow import/export outside modules */
allowImportExportEverywhere?: boolean;
/** Allow return statements outside functions */
allowReturnOutsideFunction?: boolean;
/** Allow undeclared exports */
allowUndeclaredExports?: boolean;
/** Create parent references on AST nodes */
createParenthesizedExpressions?: boolean;
/** Track error recovery information */
errorRecovery?: boolean;
/** Add location information to nodes */
ranges?: boolean;
/** Include token list in result */
tokens?: boolean;
/** Strict mode parsing */
strictMode?: boolean;
/** Start line number (default: 1) */
startLine?: number;
/** Start column number (default: 0) */
startColumn?: number;
}
type ParserPlugin =
| "jsx"
| "typescript"
| "flow"
| "decorators"
| "classProperties"
| "classPrivateProperties"
| "classPrivateMethods"
| "classStaticBlock"
| "asyncGenerators"
| "functionBind"
| "exportDefaultFrom"
| "exportNamespaceFrom"
| "dynamicImport"
| "nullishCoalescingOperator"
| "optionalChaining"
| "importMeta"
| "topLevelAwait"
| "importAssertions"
| "importReflection"
| "bigInt"
| "optionalCatchBinding"
| "throwExpressions"
| "pipelineOperator"
| "recordAndTuple"
| "doExpressions"
| "regexpUnicodeSets"
| ["decorators", { decoratorsBeforeExport?: boolean }]
| ["pipelineOperator", { proposal: "minimal" | "smart" | "fsharp" }]
| ["recordAndTuple", { syntaxType: "bar" | "hash" }]
| ["flow", { all?: boolean; enums?: boolean }]
| ["typescript", {
dts?: boolean;
disallowAmbiguousJSXLike?: boolean;
allowNamespaces?: boolean;
}];
```
**Usage Examples:**
```typescript
import { parseSync } from "@babel/core";
// TypeScript with decorators
const decoratorAst = parseSync(`
@Component({
selector: 'app-example'
})
class ExampleComponent {
@Input() value: string;
@HostListener('click')
onClick() {}
}
`, {
sourceType: "module",
plugins: [
"typescript",
["decorators", { decoratorsBeforeExport: true }]
]
});
// Flow type annotations
const flowAst = parseSync(`
type User = {
name: string,
age: number
};
function greetUser(user: User): string {
return \`Hello \${user.name}\`;
}
`, {
sourceType: "module",
plugins: [["flow", { all: true }]]
});
// Modern JavaScript features
const modernAst = parseSync(`
class APIClient {
#baseUrl = 'https://api.example.com';
async getData() {
const response = await fetch(\`\${this.#baseUrl}/data\`);
return response?.json() ?? null;
}
static {
console.log('APIClient initialized');
}
}
`, {
sourceType: "module",
plugins: [
"classPrivateProperties",
"classPrivateMethods",
"classStaticBlock",
"nullishCoalescingOperator",
"optionalChaining",
"topLevelAwait"
]
});
```
## AST Structure
The parsing result is a Babel AST with the following structure:
```typescript { .api }
interface File {
type: "File";
/** The program node containing all top-level statements */
program: Program;
/** Comments found in the source code */
comments: Comment[];
/** Tokens if tokens: true was specified */
tokens?: Token[];
/** Source location information */
loc?: SourceLocation;
/** Start and end positions */
start?: number;
end?: number;
}
interface Program {
type: "Program";
/** Top-level statements and declarations */
body: Statement[];
/** Directive nodes (like "use strict") */
directives: Directive[];
/** Source type that was detected/specified */
sourceType: "script" | "module";
/** Source location information */
loc?: SourceLocation;
}
interface SourceLocation {
/** Starting position */
start: Position;
/** Ending position */
end: Position;
/** Original filename */
filename?: string;
/** Identifier name for anonymous sources */
identifierName?: string;
}
interface Position {
/** Line number (1-based) */
line: number;
/** Column number (0-based) */
column: number;
/** Character index in source */
index?: number;
}
interface Comment {
type: "CommentBlock" | "CommentLine";
/** Comment text content */
value: string;
/** Source location */
loc?: SourceLocation;
/** Start and end positions */
start?: number;
end?: number;
}
```
## Working with ASTs
Common patterns for working with parsed ASTs:
```typescript
import { parseSync } from "@babel/core";
import traverse from "@babel/traverse";
import * as t from "@babel/types";
const code = `
function add(a, b) {
return a + b;
}
const multiply = (x, y) => x * y;
`;
const ast = parseSync(code, { sourceType: "module" });
// Traverse the AST
traverse(ast, {
// Visit all function declarations
FunctionDeclaration(path) {
console.log("Function name:", path.node.id.name);
console.log("Parameter count:", path.node.params.length);
},
// Visit all arrow functions
ArrowFunctionExpression(path) {
console.log("Arrow function found");
// Convert to regular function
const params = path.node.params;
const body = t.isExpression(path.node.body)
? t.blockStatement([t.returnStatement(path.node.body)])
: path.node.body;
path.replaceWith(
t.functionExpression(null, params, body)
);
}
});
// Check node types
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node)) {
console.log("Identifier:", path.node.name);
}
if (t.isStringLiteral(path.node)) {
console.log("String:", path.node.value);
}
}
});
```
## Error Handling
Parse functions may throw errors for invalid syntax:
```typescript
import { parseSync } from "@babel/core";
try {
const ast = parseSync("const x = ;", {
sourceType: "module"
});
} catch (error) {
if (error.code === "BABEL_PARSE_ERROR") {
console.error("Parse error:", error.message);
console.error("Location:", error.loc); // { line: 1, column: 10 }
console.error("Position:", error.pos); // Character position
}
}
// Handle missing plugins
try {
const tsAst = parseSync("const x: number = 42;", {
sourceType: "module"
// Missing "typescript" plugin
});
} catch (error) {
console.error("Missing plugin:", error.message);
// "This experimental syntax requires enabling the parser plugin: 'typescript'"
}
```
## Integration with Other Babel APIs
Parsed ASTs can be used with other Babel functions:
```typescript
import { parseSync, transformFromAstSync, traverse } from "@babel/core";
// Parse -> Modify -> Transform workflow
const code = `const greeting = name => \`Hello \${name}\`;`;
// 1. Parse to AST
const ast = parseSync(code, {
sourceType: "module",
plugins: ["templateLiterals"]
});
// 2. Modify AST
traverse(ast, {
TemplateLiteral(path) {
// Convert template literal to concatenation
// This is just an example - in practice use appropriate plugins
}
});
// 3. Transform to code
const result = transformFromAstSync(ast, code, {
presets: ["@babel/preset-env"]
});
console.log(result.code);
```

View file

@ -0,0 +1,327 @@
# Code Transformation
Core JavaScript transformation functionality for converting modern JavaScript code into backward-compatible versions using Babel plugins and presets. Supports string-based, file-based, and AST-based transformation workflows.
## Capabilities
### String Transformation
Transform JavaScript code from strings with full plugin and preset support.
```typescript { .api }
/**
* Transform JavaScript code synchronously
* @param code - JavaScript source code to transform
* @param opts - Transformation options including plugins, presets, and parser settings
* @returns Transformation result with code, source map, and optional AST
*/
function transformSync(code: string, opts?: InputOptions): FileResult | null;
/**
* Transform JavaScript code asynchronously
* @param code - JavaScript source code to transform
* @param opts - Transformation options including plugins, presets, and parser settings
* @returns Promise resolving to transformation result
*/
function transformAsync(code: string, opts?: InputOptions): Promise<FileResult | null>;
/**
* Transform JavaScript code with callback (legacy API, deprecated in Babel 8)
* @param code - JavaScript source code to transform
* @param opts - Transformation options
* @param callback - Callback function receiving error and result
*/
function transform(
code: string,
opts: InputOptions | null | undefined,
callback: FileResultCallback
): void;
function transform(code: string, callback: FileResultCallback): void;
type FileResultCallback = (err: Error | null, result: FileResult | null) => void;
```
**Usage Examples:**
```typescript
import { transformSync, transformAsync } from "@babel/core";
// Synchronous transformation
const result = transformSync(`
const getMessage = () => "Hello World";
class User {
constructor(name) {
this.name = name;
}
}
`, {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-classes"]
});
console.log(result.code);
// Output: ES5 compatible code
// Asynchronous transformation
const asyncResult = await transformAsync(`
import { useState } from 'react';
export const Component = () => <div>Hello</div>;
`, {
presets: ["@babel/preset-react", "@babel/preset-env"],
filename: "component.jsx"
});
console.log(asyncResult.code);
```
### File Transformation
Transform JavaScript files directly from the filesystem.
```typescript { .api }
/**
* Transform JavaScript file synchronously
* @param filename - Path to JavaScript file to transform
* @param opts - Transformation options (filename will be added automatically)
* @returns Transformation result with code, source map, and optional AST
*/
function transformFileSync(filename: string, opts?: InputOptions): FileResult | null;
/**
* Transform JavaScript file asynchronously
* @param filename - Path to JavaScript file to transform
* @param opts - Transformation options (filename will be added automatically)
* @returns Promise resolving to transformation result
*/
function transformFileAsync(filename: string, opts?: InputOptions): Promise<FileResult | null>;
/**
* Transform JavaScript file with callback
* @param filename - Path to JavaScript file to transform
* @param opts - Transformation options
* @param callback - Callback function receiving error and result
*/
function transformFile(
filename: string,
opts: InputOptions | null | undefined,
callback: FileResultCallback
): void;
function transformFile(filename: string, callback: FileResultCallback): void;
```
**Usage Examples:**
```typescript
import { transformFileSync, transformFileAsync } from "@babel/core";
// Synchronous file transformation
const result = transformFileSync("./src/app.js", {
presets: ["@babel/preset-env"],
sourceMaps: true
});
if (result) {
console.log("Transformed:", result.code);
console.log("Source map:", result.map);
}
// Asynchronous file transformation
const asyncResult = await transformFileAsync("./src/component.tsx", {
presets: ["@babel/preset-typescript", "@babel/preset-react"],
plugins: ["@babel/plugin-transform-runtime"]
});
```
### AST Transformation
Transform JavaScript code from existing Abstract Syntax Trees (ASTs).
```typescript { .api }
/**
* Transform from AST synchronously
* @param ast - Babel AST (File or Program node)
* @param code - Original source code string for source map generation
* @param opts - Transformation options
* @returns Transformation result with code, source map, and optional AST
*/
function transformFromAstSync(
ast: AstRoot,
code: string,
opts?: InputOptions
): FileResult | null;
/**
* Transform from AST asynchronously
* @param ast - Babel AST (File or Program node)
* @param code - Original source code string for source map generation
* @param opts - Transformation options
* @returns Promise resolving to transformation result
*/
function transformFromAstAsync(
ast: AstRoot,
code: string,
opts?: InputOptions
): Promise<FileResult | null>;
/**
* Transform from AST with callback (legacy API, deprecated in Babel 8)
* @param ast - Babel AST (File or Program node)
* @param code - Original source code string
* @param opts - Transformation options
* @param callback - Callback function receiving error and result
*/
function transformFromAst(
ast: AstRoot,
code: string,
opts: InputOptions | null | undefined,
callback: FileResultCallback
): void;
function transformFromAst(
ast: AstRoot,
code: string,
callback: FileResultCallback
): void;
type AstRoot = import("@babel/types").File | import("@babel/types").Program;
```
**Usage Examples:**
```typescript
import { parseSync, transformFromAstSync } from "@babel/core";
// Parse then transform
const code = `const x = () => 42;`;
const ast = parseSync(code, {
sourceType: "module",
plugins: ["jsx"]
});
const result = transformFromAstSync(ast, code, {
presets: ["@babel/preset-env"]
});
console.log(result.code);
// Output: Transformed code from the AST
// Modify AST before transformation
import traverse from "@babel/traverse";
import * as t from "@babel/types";
traverse(ast, {
ArrowFunctionExpression(path) {
// Convert arrow function to regular function
path.replaceWith(
t.functionExpression(null, path.node.params,
t.blockStatement([t.returnStatement(path.node.body)])
)
);
}
});
const modifiedResult = transformFromAstSync(ast, code, {
presets: ["@babel/preset-env"]
});
```
## Transformation Result
All transformation functions return a `FileResult` object containing the transformed code and metadata.
```typescript { .api }
interface FileResult {
/** Transformed JavaScript code, null if transformation was skipped */
code: string | null;
/** Source map object for debugging, null if source maps disabled */
map: object | null;
/** AST object if ast: true option was provided, null otherwise */
ast: object | null;
/** Metadata collected during transformation including plugin information */
metadata: {
/** Modules that were processed during transformation */
modules?: {
imports: Array<{
source: string;
imported: string[];
specifiers: any[];
}>;
exports: Array<{
exported: string[];
specifiers: any[];
}>;
};
/** List of external helper functions that were used */
externalHelpers?: string[];
/** Plugin-specific metadata */
[pluginName: string]: any;
};
}
```
## Common Transformation Options
Key options for controlling the transformation process:
```typescript { .api }
interface TransformationOptions {
/** Plugins to apply during transformation */
plugins?: Array<string | [string, any] | PluginFunction | [PluginFunction, any]>;
/** Presets to apply during transformation (applied before plugins) */
presets?: Array<string | [string, any] | PresetFunction | [PresetFunction, any]>;
/** Include AST in result (default: false) */
ast?: boolean;
/** Generate source maps: false, true, "inline", or "both" */
sourceMaps?: boolean | "inline" | "both";
/** Compact output: true, false, or "auto" (default: "auto") */
compact?: boolean | "auto";
/** Environment name for conditional configuration */
envName?: string;
/** Override source filename in source maps and error messages */
filename?: string;
/** Parser options passed to @babel/parser */
parserOpts?: {
sourceType?: "script" | "module" | "unambiguous";
allowImportExportEverywhere?: boolean;
allowReturnOutsideFunction?: boolean;
plugins?: string[];
strictMode?: boolean;
ranges?: boolean;
tokens?: boolean;
};
/** Generator options passed to @babel/generator */
generatorOpts?: {
/** Retain parentheses around expressions */
retainLines?: boolean;
/** Compact whitespace */
compact?: boolean;
/** Number of spaces for indentation */
indent?: number;
/** Quote style: "single" or "double" */
quotes?: "single" | "double";
};
}
```
## Error Handling
Transformation functions may throw errors for various reasons:
```typescript
import { transformSync } from "@babel/core";
try {
const result = transformSync("invalid syntax {{", {
presets: ["@babel/preset-env"]
});
} catch (error) {
if (error.code === "BABEL_PARSE_ERROR") {
console.error("Parse error:", error.message);
console.error("Location:", error.loc);
} else if (error.code === "BABEL_TRANSFORM_ERROR") {
console.error("Transform error:", error.message);
console.error("Plugin:", error.plugin);
} else {
console.error("Other error:", error.message);
}
}
```

View file

@ -0,0 +1,564 @@
# Utilities and Constants
Helper functions, constants, and re-exported APIs from the Babel ecosystem. Includes version information, file extensions, environment detection, plugin resolution, and access to the complete Babel toolchain.
## Capabilities
### Version and Constants
Version information and recommended file extensions for Babel processing.
```typescript { .api }
/**
* Current version of @babel/core package
*/
const version: string;
/**
* Recommended set of compilable file extensions
* Not used in @babel/core directly, but meant as an easy source for tooling
*/
const DEFAULT_EXTENSIONS: readonly [".js", ".jsx", ".es6", ".es", ".mjs", ".cjs"];
```
**Usage Examples:**
```typescript
import { version, DEFAULT_EXTENSIONS } from "@babel/core";
console.log("Babel version:", version); // "7.26.10"
console.log("Default extensions:", DEFAULT_EXTENSIONS);
// [".js", ".jsx", ".es6", ".es", ".mjs", ".cjs"]
// Use in build tools
const shouldProcess = (filename) => {
return DEFAULT_EXTENSIONS.some(ext => filename.endsWith(ext));
};
console.log(shouldProcess("app.js")); // true
console.log(shouldProcess("styles.css")); // false
```
### Environment Detection
Detect and resolve the current Babel environment.
```typescript { .api }
/**
* Get the current Babel environment name
* @param defaultValue - Default environment if none specified (default: "development")
* @returns Environment name from BABEL_ENV, NODE_ENV, or default value
*/
function getEnv(defaultValue?: string): string;
```
**Usage Examples:**
```typescript
import { getEnv } from "@babel/core";
// Without environment variables set
console.log(getEnv()); // "development"
console.log(getEnv("production")); // "production"
// With NODE_ENV=test
process.env.NODE_ENV = "test";
console.log(getEnv()); // "test"
// With BABEL_ENV=staging (takes precedence over NODE_ENV)
process.env.BABEL_ENV = "staging";
console.log(getEnv()); // "staging"
// Use in configuration
const isProd = getEnv() === "production";
const isDev = getEnv() === "development";
```
### Plugin and Preset Resolution
Resolve plugin and preset file paths (legacy APIs for backward compatibility).
```typescript { .api }
/**
* Resolve plugin file path (legacy API)
* @param name - Plugin name or path
* @param dirname - Directory to resolve from
* @returns Resolved file path
*/
function resolvePlugin(name: string, dirname: string): string;
/**
* Resolve preset file path (legacy API)
* @param name - Preset name or path
* @param dirname - Directory to resolve from
* @returns Resolved file path
*/
function resolvePreset(name: string, dirname: string): string;
```
**Usage Examples:**
```typescript
import { resolvePlugin, resolvePreset } from "@babel/core";
// Resolve official plugins
const pluginPath = resolvePlugin("@babel/plugin-transform-arrow-functions", __dirname);
console.log(pluginPath); // "/path/to/node_modules/@babel/plugin-transform-arrow-functions/lib/index.js"
// Resolve official presets
const presetPath = resolvePreset("@babel/preset-env", process.cwd());
console.log(presetPath); // "/path/to/node_modules/@babel/preset-env/lib/index.js"
// Resolve relative paths
const localPlugin = resolvePlugin("./plugins/custom-plugin", __dirname);
console.log(localPlugin); // "/current/dir/plugins/custom-plugin.js"
// Use in plugin loading
function loadPlugin(name, dirname) {
try {
const pluginPath = resolvePlugin(name, dirname);
return require(pluginPath);
} catch (error) {
console.error(`Failed to load plugin ${name}:`, error.message);
}
}
```
### External Helper Generation
Generate external Babel helper functions for runtime optimization.
```typescript { .api }
/**
* Build external helper functions as a single module
* @param whitelist - Array of helper names to include, or undefined for all
* @param outputType - Output format: "global", "umd", "var", or function
* @returns Generated helper code as string
*/
function buildExternalHelpers(
whitelist?: string[],
outputType?: "global" | "umd" | "var" | ((name: string) => string)
): string;
```
**Usage Examples:**
```typescript
import { buildExternalHelpers } from "@babel/core";
// Generate all helpers as global variables
const allHelpers = buildExternalHelpers(undefined, "global");
console.log(allHelpers);
// Output: Global helper functions for all Babel runtime helpers
// Generate specific helpers only
const specificHelpers = buildExternalHelpers([
"_classCallCheck",
"_createClass",
"_inherits"
], "umd");
console.log(specificHelpers);
// Output: UMD module with class-related helpers only
// Generate with custom output format
const customHelpers = buildExternalHelpers(
["_asyncToGenerator", "_awaitAsyncGenerator"],
(name) => `window.BabelHelpers.${name.slice(1)}`
);
console.log(customHelpers);
// Output: Helpers assigned to window.BabelHelpers
// Use in build process
const fs = require("fs");
const helpers = buildExternalHelpers(undefined, "umd");
fs.writeFileSync("dist/babel-helpers.js", helpers);
```
## Re-exported APIs
Babel Core re-exports several essential APIs from other Babel packages for convenience.
### Babel Types
Complete AST node types and utilities from @babel/types.
```typescript { .api }
/**
* Complete @babel/types API for AST manipulation
* Includes all node builders, validators, and utilities
*/
import * as types from "@babel/types";
// Re-exported as namespace
export * as types from "@babel/types";
```
**Usage Examples:**
```typescript
import { types as t } from "@babel/core";
// or: import * as t from "@babel/core";
// Create AST nodes
const identifier = t.identifier("myVariable");
const stringLiteral = t.stringLiteral("Hello World");
const callExpression = t.callExpression(
t.identifier("console.log"),
[stringLiteral]
);
// Validate node types
if (t.isIdentifier(identifier)) {
console.log("Name:", identifier.name);
}
if (t.isCallExpression(callExpression)) {
console.log("Callee:", callExpression.callee);
console.log("Arguments:", callExpression.arguments);
}
// Build complex structures
const functionDeclaration = t.functionDeclaration(
t.identifier("greet"),
[t.identifier("name")],
t.blockStatement([
t.returnStatement(
t.templateLiteral(
[
t.templateElement({ raw: "Hello " }, false),
t.templateElement({ raw: "!" }, true)
],
[t.identifier("name")]
)
)
])
);
```
### Babel Traverse
AST traversal utilities from @babel/traverse.
```typescript { .api }
/**
* Default traverse function for AST traversal
*/
export { default as traverse } from "@babel/traverse";
/**
* Node path type for AST traversal
*/
export type { NodePath } from "@babel/traverse";
/**
* Scope information type
*/
export type { Scope } from "@babel/traverse";
/**
* Visitor pattern type for AST traversal
*/
export type Visitor<S = unknown> = import("@babel/traverse").Visitor<S>;
```
**Usage Examples:**
```typescript
import { traverse, parseSync, types as t } from "@babel/core";
const code = `
function calculate(a, b) {
const result = a + b;
return result;
}
`;
const ast = parseSync(code, { sourceType: "module" });
// Basic traversal
traverse(ast, {
enter(path) {
console.log("Entering:", path.node.type);
},
exit(path) {
console.log("Exiting:", path.node.type);
}
});
// Specific node visitors
traverse(ast, {
FunctionDeclaration(path) {
console.log("Function:", path.node.id.name);
console.log("Parameters:", path.node.params.map(p => p.name));
},
VariableDeclarator(path) {
if (t.isIdentifier(path.node.id)) {
console.log("Variable:", path.node.id.name);
}
},
BinaryExpression(path) {
console.log("Operation:", path.node.operator);
console.log("Left:", path.node.left);
console.log("Right:", path.node.right);
}
});
// Path manipulation
traverse(ast, {
Identifier(path) {
if (path.node.name === "result") {
path.node.name = "output";
}
}
});
```
### Babel Template
Template string to AST conversion from @babel/template.
```typescript { .api }
/**
* Template function for converting template strings to AST nodes
*/
export { default as template } from "@babel/template";
```
**Usage Examples:**
```typescript
import { template, traverse, parseSync } from "@babel/core";
// Create template functions
const buildRequire = template(`
var %%importName%% = require(%%source%%);
`);
const buildClass = template.statement(`
class %%className%% extends %%superClass%% {
constructor(%%params%%) {
super(%%args%%);
%%body%%
}
}
`);
// Use templates to generate AST
const requireNode = buildRequire({
importName: t.identifier("lodash"),
source: t.stringLiteral("lodash")
});
const classNode = buildClass({
className: t.identifier("MyComponent"),
superClass: t.identifier("Component"),
params: [t.identifier("props")],
args: [t.identifier("props")],
body: [
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.thisExpression(), t.identifier("state")),
t.objectExpression([])
)
)
]
});
// Template with expressions
const buildConditional = template.expression(`
%%test%% ? %%consequent%% : %%alternate%%
`);
const conditional = buildConditional({
test: t.identifier("isLoggedIn"),
consequent: t.stringLiteral("Welcome"),
alternate: t.stringLiteral("Please log in")
});
```
### Babel Parser Token Types
Token types from @babel/parser for advanced parsing use cases.
```typescript { .api }
/**
* Token types from Babel parser
*/
export { tokTypes } from "@babel/parser";
```
**Usage Examples:**
```typescript
import { tokTypes, parseSync } from "@babel/core";
// Parse with tokens
const ast = parseSync("const x = 42;", {
sourceType: "module",
tokens: true
});
// Check token types
if (ast.tokens) {
ast.tokens.forEach(token => {
if (token.type === tokTypes.name) {
console.log("Identifier token:", token.value);
} else if (token.type === tokTypes.num) {
console.log("Number token:", token.value);
} else if (token.type === tokTypes._const) {
console.log("Const keyword token");
}
});
}
// Token type checking
console.log("Available token types:", Object.keys(tokTypes));
// ["num", "string", "name", "_const", "_let", "_var", ...]
```
## File Context Class
The File class provides transformation context and utilities, primarily used in plugin development and advanced transformation scenarios.
```typescript { .api }
/**
* File transformation context class
* Provides access to transformation state, metadata, and helper functions
*/
export { default as File } from "./transformation/file/file";
class File {
/** Transformation options merged from config and parameters */
opts: TransformationOptions;
/** Variable declarations at file scope */
declarations: { [name: string]: import("@babel/types").Identifier };
/** Root program path for AST traversal */
path: import("@babel/traverse").NodePath<import("@babel/types").Program>;
/** Complete file AST including program and metadata */
ast: import("@babel/types").File;
/** Root scope for the file */
scope: import("@babel/traverse").Scope;
/** Metadata collected from plugins during transformation */
metadata: { [pluginName: string]: any };
/** Original source code string */
code: string;
/** Input source map if available */
inputMap: import("convert-source-map").SourceMapConverter | null;
/** Hub interface for plugin communication and utilities */
hub: FileHub;
/** Generate a unique identifier in file scope */
generateUid(name: string): import("@babel/types").Identifier;
/** Check if identifier is available in file scope */
hasIdentifier(name: string): boolean;
/** Add import declaration to file */
addImport(source: string, importedName: string, localName?: string): import("@babel/types").Identifier;
/** Add helper function import */
addHelper(name: string): import("@babel/types").Identifier;
}
interface FileHub {
/** Reference to the current file */
file: File;
/** Get current transformed code */
getCode(): string;
/** Get file root scope */
getScope(): import("@babel/traverse").Scope;
/** Add Babel helper function and return its identifier */
addHelper(name: string): import("@babel/types").Identifier;
/** Create error with location information */
buildError<T extends import("@babel/types").Node>(
node: T,
message: string,
constructor?: typeof Error
): Error;
}
interface TransformationOptions {
/** Source filename */
filename?: string;
/** Source type */
sourceType?: "script" | "module" | "unambiguous";
/** Plugins to apply */
plugins?: any[];
/** Presets to apply */
presets?: any[];
/** Parser options */
parserOpts?: any;
/** Generator options */
generatorOpts?: any;
/** Additional transformation options */
[key: string]: any;
}
```
**Usage in Plugin Development:**
```typescript
import { PluginObj, PluginPass } from "@babel/core";
import * as t from "@babel/types";
function myPlugin(): PluginObj {
return {
visitor: {
Program(path, state: PluginPass) {
// Access file context
const file = state.file;
// Get transformation options
console.log("Filename:", file.opts.filename);
console.log("Source type:", file.opts.sourceType);
// Add helper function
const helperIdentifier = file.addHelper("classCallCheck");
// Generate unique identifier
const uniqueId = file.generateUid("temp");
// Add metadata for other plugins
file.metadata.myPlugin = {
processedNodes: 0,
addedHelpers: [helperIdentifier.name]
};
// Access file scope
const hasConsole = file.scope.hasBinding("console");
console.log("Console available:", hasConsole);
},
ClassDeclaration(path, state: PluginPass) {
const file = state.file;
// Increment counter in metadata
if (!file.metadata.myPlugin) {
file.metadata.myPlugin = { processedNodes: 0 };
}
file.metadata.myPlugin.processedNodes++;
// Create error with file context
if (path.node.id === null) {
throw file.hub.buildError(
path.node,
"Anonymous classes are not supported"
);
}
}
}
};
}
```
The File class is essential for plugin authors who need to:
1. **Access transformation context** - filename, options, and configuration
2. **Manage helper functions** - automatically import required runtime helpers
3. **Generate unique identifiers** - avoid naming conflicts in transformed code
4. **Share data between plugins** - using the metadata system
5. **Handle errors with context** - provide meaningful error messages with location info
6. **Access file-level scope information** - understand available bindings and references

View file

@ -0,0 +1,7 @@
{
"name": "tessl/npm-babel--core",
"version": "7.26.0",
"docs": "docs/index.md",
"describes": "pkg:npm/@babel/core@7.26.10",
"summary": "Babel compiler core providing programmatic APIs for JavaScript code transformation, parsing, and configuration."
}

View file

@ -0,0 +1,376 @@
# @babel/preset-typescript
@babel/preset-typescript is a Babel preset that enables TypeScript compilation through Babel's plugin pipeline. It configures the necessary plugins to transform TypeScript syntax into JavaScript while maintaining compatibility with Babel's ecosystem and build tools.
## Package Information
- **Package Name**: @babel/preset-typescript
- **Package Type**: npm
- **Language**: TypeScript/JavaScript
- **Installation**: `npm install --save-dev @babel/preset-typescript`
## Core Imports
```javascript
// babel.config.js
module.exports = {
presets: ['@babel/preset-typescript']
};
```
With options:
```javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-typescript', {
allowNamespaces: true,
onlyRemoveTypeImports: true,
optimizeConstEnums: false
}]
]
};
```
For programmatic usage:
```javascript
const presetTypescript = require('@babel/preset-typescript').default;
// or
import presetTypescript from '@babel/preset-typescript';
```
## Basic Usage
```javascript
// babel.config.js - Basic configuration
module.exports = {
presets: ['@babel/preset-typescript']
};
```
```javascript
// babel.config.js - Advanced configuration
module.exports = {
presets: [
['@babel/preset-typescript', {
// Allow TypeScript namespaces (default: true)
allowNamespaces: true,
// Only remove type-only imports (default: true in Babel 8)
onlyRemoveTypeImports: true,
// Optimize const enum transformations (default: false)
optimizeConstEnums: false,
// Custom JSX pragma (default: "React")
jsxPragma: "React",
// Custom JSX fragment pragma (default: "React.Fragment")
jsxPragmaFrag: "React.Fragment",
// Rewrite TypeScript import extensions (default: false)
rewriteImportExtensions: false
}]
]
};
```
## Architecture
The preset is built around several key components:
- **Core Preset Function**: Main function that configures TypeScript transformation plugins based on options and file extensions
- **Options Normalizer**: Validates and normalizes configuration options with Babel version-specific behavior
- **Import Rewriter Plugin**: Optional plugin that rewrites TypeScript import extensions to JavaScript equivalents
- **Plugin Configuration**: Automatically configures `@babel/plugin-transform-typescript`, JSX syntax support, and CommonJS transformation based on file types
## Capabilities
### Preset Configuration
The main preset function that configures TypeScript transformation for Babel. The preset is exported as the default export wrapped in Babel's `declarePreset` utility.
```typescript { .api }
// The actual export from @babel/preset-typescript
const preset: (api: PresetAPI, options?: Options, dirname?: string) => PresetObject;
// From @babel/core
interface PresetObject {
plugins?: PluginList;
presets?: PresetList;
overrides?: Array<PresetObject>;
env?: { [envName: string]: PresetObject };
ignore?: IgnoreList;
only?: IgnoreList;
test?: ConfigApplicableTest;
include?: ConfigApplicableTest;
exclude?: ConfigApplicableTest;
}
// The preset returns a configuration with plugins and overrides
interface PresetResult {
plugins: Array<string | [string, any]>;
overrides: Array<{
test?: RegExp | ((filename?: string) => boolean);
sourceType?: "module" | "unambiguous";
plugins: Array<string | [string, any]>;
}>;
}
```
### Options Configuration
Configuration options for customizing TypeScript transformation behavior.
```typescript { .api }
interface Options {
/** Ignore file extensions when determining file type */
ignoreExtensions?: boolean;
/** Allow TypeScript declare fields (Babel 7 only) */
allowDeclareFields?: boolean;
/** Allow TypeScript namespaces (default: true) */
allowNamespaces?: boolean;
/** Disallow ambiguous JSX-like syntax */
disallowAmbiguousJSXLike?: boolean;
/** JSX pragma to use (default: "React") */
jsxPragma?: string;
/** JSX fragment pragma (default: "React.Fragment") */
jsxPragmaFrag?: string;
/** Only remove type-only imports */
onlyRemoveTypeImports?: boolean;
/** Optimize const enums transformation */
optimizeConstEnums?: boolean;
/** Rewrite TypeScript import extensions to JavaScript */
rewriteImportExtensions?: boolean;
/** Handle all file extensions (deprecated in Babel 8) */
allExtensions?: boolean;
/** Force JSX parsing (deprecated in Babel 8) */
isTSX?: boolean;
}
```
### Option Normalization
**Internal function** that validates and normalizes preset options. This function is not exported from the main package and is only used internally by the preset.
```typescript { .api }
// Internal function - NOT exported from @babel/preset-typescript
// Located in: src/normalize-options.ts
function normalizeOptions(options?: Options): Required<Options>;
```
**Usage:** Called internally by the preset to validate and apply default values to user-provided options. This function handles Babel version-specific option validation and provides helpful error messages for deprecated options.
### Import Extension Rewriting
**Internal plugin** that rewrites TypeScript import extensions to JavaScript equivalents. This plugin is not exported from the main package and is only used internally when `rewriteImportExtensions: true` is specified.
```typescript { .api }
// Internal plugin - NOT exported from @babel/preset-typescript
// Located in: src/plugin-rewrite-ts-imports.ts
function pluginRewriteTSImports(): PluginObject;
// From @babel/core
interface PluginObject {
name?: string;
visitor: Visitor;
pre?: (state: any) => void;
post?: (state: any) => void;
manipulateOptions?: (opts: any, parserOpts: any) => void;
}
```
**Transformation Examples:**
- `./module.ts``./module.js`
- `./component.tsx``./component.jsx` (or `.js` if JSX preservation is disabled)
- `./module.mts``./module.mjs`
- `./module.cts``./module.cjs`
- `./types.d.ts``./types.d.ts` (preserved)
## File Extension Handling
The preset automatically applies different plugin configurations based on file extensions:
### TypeScript Files (.ts)
Standard TypeScript files receive basic TypeScript transformation:
```javascript
// Configuration applied for .ts files
{
plugins: [
['@babel/plugin-transform-typescript', {
allowNamespaces: true,
disallowAmbiguousJSXLike: false,
// ... other options
}]
]
}
```
### TypeScript JSX Files (.tsx)
TypeScript files with JSX receive TypeScript transformation plus JSX syntax support:
```javascript
// Configuration applied for .tsx files
{
plugins: [
['@babel/plugin-transform-typescript', {
isTSX: true,
allowNamespaces: true,
// ... other options
}],
'@babel/plugin-syntax-jsx'
]
}
```
### ES Module TypeScript (.mts)
TypeScript ES module files with strict module type checking:
```javascript
// Configuration applied for .mts files
{
sourceType: "module",
plugins: [
['@babel/plugin-transform-typescript', {
disallowAmbiguousJSXLike: true,
// ... other options
}]
]
}
```
### CommonJS TypeScript (.cts)
TypeScript CommonJS files with automatic CommonJS transformation:
```javascript
// Configuration applied for .cts files
{
sourceType: "unambiguous",
plugins: [
['@babel/plugin-transform-modules-commonjs', {
allowTopLevelThis: true
}],
['@babel/plugin-transform-typescript', {
disallowAmbiguousJSXLike: true,
// ... other options
}]
]
}
```
## Version Compatibility
### Babel 7 Compatibility
Babel 7 supports additional legacy options:
- `allowDeclareFields`: Controls TypeScript declare field support
- `allExtensions`: Forces handling of all file extensions
- `isTSX`: Forces JSX parsing for all files
### Babel 8 Compatibility
Babel 8 removes deprecated options and enforces stricter validation:
- Removes `allowDeclareFields`, `allExtensions`, and `isTSX` options
- Stricter option validation with helpful error messages
- Enhanced Node.js version requirements (>=20.19.0 || >=22.12.0)
## Plugin Dependencies
The preset automatically configures these Babel plugins:
- **@babel/plugin-transform-typescript**: Core TypeScript syntax transformation
- **@babel/plugin-syntax-jsx**: JSX syntax parsing support
- **@babel/plugin-transform-modules-commonjs**: CommonJS module transformation (for .cts files)
## Common Configuration Examples
### React TypeScript Project
```javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-typescript', {
jsxPragma: 'React',
jsxPragmaFrag: 'React.Fragment'
}],
'@babel/preset-react'
]
};
```
### Node.js TypeScript Project
```javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-typescript', {
allowNamespaces: true,
optimizeConstEnums: true
}],
['@babel/preset-env', {
targets: { node: 'current' }
}]
]
};
```
### Strict TypeScript Configuration
```javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-typescript', {
onlyRemoveTypeImports: true,
disallowAmbiguousJSXLike: true,
ignoreExtensions: true
}]
]
};
```
### Import Extension Rewriting
```javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-typescript', {
rewriteImportExtensions: true
}]
]
};
```
This configuration transforms:
```typescript
// Input TypeScript
import { helper } from './utils.ts';
import Component from './Component.tsx';
// Output JavaScript
import { helper } from './utils.js';
import Component from './Component.jsx';
```

View file

@ -0,0 +1,7 @@
{
"name": "tessl/npm-babel--preset-typescript",
"version": "7.27.0",
"docs": "docs/index.md",
"describes": "pkg:npm/@babel/preset-typescript@7.27.1",
"summary": "Babel preset for TypeScript compilation through Babel's plugin pipeline"
}

View file

@ -0,0 +1,253 @@
# Database Management
Core database connection and lifecycle management functionality for creating, configuring, and controlling SQLite database connections.
## Capabilities
### Database Constructor
Creates a new database connection with comprehensive configuration options.
```javascript { .api }
/**
* Creates a new database connection
* @param {string|Buffer} filename - Database file path, ":memory:" for in-memory, or Buffer for serialized database
* @param {Object} [options] - Database configuration options
* @returns {Database} Database instance
*/
function Database(filename, options);
interface DatabaseOptions {
readonly?: boolean; // Open in readonly mode (default: false)
fileMustExist?: boolean; // Throw error if file doesn't exist (default: false)
timeout?: number; // Timeout in ms for locked database (default: 5000)
verbose?: Function; // Function called with every SQL execution
nativeBinding?: string | Object; // Path to native binding or binding object
}
```
**Usage Examples:**
```javascript
const Database = require('better-sqlite3');
// Create/open a file database
const db = new Database('myapp.db');
// Create in-memory database
const memDb = new Database(':memory:');
// Create temporary database (deleted when closed)
const tempDb = new Database('');
// Open readonly database
const readOnlyDb = new Database('data.db', { readonly: true });
// Database with timeout and verbose logging
const verboseDb = new Database('app.db', {
timeout: 10000,
verbose: console.log
});
// Database that must exist (throws if file missing)
const existingDb = new Database('existing.db', { fileMustExist: true });
// Create from serialized buffer
const serializedBuffer = fs.readFileSync('backup.db');
const restoredDb = new Database(serializedBuffer);
```
### Direct SQL Execution
Execute SQL statements directly without prepared statements.
```javascript { .api }
/**
* Execute SQL string directly (no prepared statement)
* @param {string} sql - SQL query string (can contain multiple statements)
* @returns {Database} Database instance for chaining
*/
exec(sql);
```
**Usage Examples:**
```javascript
// Create tables and initial data
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
INSERT OR IGNORE INTO users (name, email) VALUES
('Admin', 'admin@example.com'),
('Guest', 'guest@example.com');
`);
// Drop and recreate table
db.exec('DROP TABLE IF EXISTS temp_data; CREATE TABLE temp_data (value TEXT);');
```
### Database Connection Management
Close database connections and manage connection lifecycle.
```javascript { .api }
/**
* Close database connection
* @returns {Database} Database instance for chaining
*/
close();
```
**Usage Examples:**
```javascript
// Proper database cleanup
try {
const db = new Database('myapp.db');
// Use database...
} finally {
// Always close the database
db.close();
}
// Check if database is still open
console.log(db.open); // false after closing
```
### Extension Loading
Load SQLite extensions to add functionality.
```javascript { .api }
/**
* Load SQLite extension
* @param {string} path - Path to extension file (.dll, .so, .dylib)
* @param {string} [entrypoint] - Entry point function name (optional)
* @returns {Database} Database instance for chaining
*/
loadExtension(path, entrypoint);
```
**Usage Examples:**
```javascript
// Load extension with automatic entry point
db.loadExtension('./extensions/json1.so');
// Load extension with specific entry point
db.loadExtension('./extensions/fts5.so', 'sqlite3_fts5_init');
// Load multiple extensions
db.loadExtension('./extensions/rtree.so')
.loadExtension('./extensions/soundex.so');
```
### Database Configuration
Configure database-wide settings for integer handling and safety modes.
```javascript { .api }
/**
* Set default safe integer handling for new statements
* @param {boolean} enabled - Enable safe integers by default
* @returns {Database} Database instance for chaining
*/
defaultSafeIntegers(enabled);
/**
* Enable/disable unsafe mode (disables certain safety checks)
* @param {boolean} enabled - Enable unsafe mode
* @returns {Database} Database instance for chaining
*/
unsafeMode(enabled);
```
**Usage Examples:**
```javascript
// Enable safe integers for large numbers
db.defaultSafeIntegers(true);
// All new prepared statements will use safe integers
const stmt = db.prepare('SELECT very_large_number FROM table');
const result = stmt.get(); // Returns BigInt for large integers
// Enable unsafe mode for maximum performance (use with caution)
db.unsafeMode(true);
```
### Database Properties
Read-only properties providing database connection information.
```javascript { .api }
interface DatabaseProperties {
readonly name: string; // Database filename or ":memory:"
readonly open: boolean; // Whether connection is open
readonly inTransaction: boolean; // Whether currently in transaction
readonly readonly: boolean; // Whether database is readonly
readonly memory: boolean; // Whether database is in-memory
}
```
**Usage Examples:**
```javascript
const db = new Database('myapp.db', { readonly: true });
console.log(db.name); // "myapp.db"
console.log(db.open); // true
console.log(db.readonly); // true
console.log(db.memory); // false
console.log(db.inTransaction); // false
// Properties update automatically
const transaction = db.transaction(() => {
console.log(db.inTransaction); // true during transaction
});
transaction();
console.log(db.inTransaction); // false after transaction
db.close();
console.log(db.open); // false
```
## Error Handling
```javascript { .api }
class SqliteError extends Error {
constructor(message, code);
readonly name: string; // Always "SqliteError"
readonly code: string; // SQLite error code (e.g., "SQLITE_CONSTRAINT")
readonly message: string; // Descriptive error message
}
```
**Common Error Scenarios:**
```javascript
try {
const db = new Database('nonexistent.db', { fileMustExist: true });
} catch (error) {
if (error instanceof Database.SqliteError) {
console.log(error.code); // "SQLITE_CANTOPEN"
console.log(error.message); // "Cannot open database..."
}
}
try {
db.exec('INVALID SQL SYNTAX');
} catch (error) {
console.log(error.code); // "SQLITE_ERROR"
console.log(error.message); // "near \"INVALID\": syntax error"
}
```

View file

@ -0,0 +1,362 @@
# Database Utilities
Utility functions for database introspection, backup, serialization, and configuration management.
## Capabilities
### PRAGMA Commands
Execute PRAGMA commands for database configuration and introspection.
```javascript { .api }
/**
* Execute PRAGMA commands for database configuration and introspection
* @param {string} source - PRAGMA command string (without "PRAGMA" prefix)
* @param {Object} [options] - Execution options
* @returns {Array|any} Results array or single value if simple mode
*/
pragma(source, options);
interface PragmaOptions {
simple?: boolean; // Return single value instead of array (default: false)
}
```
**Usage Examples:**
```javascript
// Get database information
const userVersion = db.pragma('user_version', { simple: true });
console.log(userVersion); // Returns number directly
// Get table information
const tableInfo = db.pragma('table_info(users)');
console.log(tableInfo);
// Returns array of column descriptors:
// [
// { cid: 0, name: 'id', type: 'INTEGER', notnull: 0, dflt_value: null, pk: 1 },
// { cid: 1, name: 'name', type: 'TEXT', notnull: 1, dflt_value: null, pk: 0 },
// ...
// ]
// Get foreign key information
const foreignKeys = db.pragma('foreign_key_list(orders)');
foreignKeys.forEach(fk => {
console.log(`${fk.from} references ${fk.table}.${fk.to}`);
});
// Database configuration
db.pragma('journal_mode = WAL'); // Enable WAL mode
db.pragma('synchronous = NORMAL'); // Set synchronous mode
db.pragma('cache_size = 10000'); // Set cache size
// Get current settings
const journalMode = db.pragma('journal_mode', { simple: true });
const pageSize = db.pragma('page_size', { simple: true });
const cacheSize = db.pragma('cache_size', { simple: true });
console.log(`Journal mode: ${journalMode}, Page size: ${pageSize}, Cache size: ${cacheSize}`);
```
### Database Backup
Create backups of the database to files with progress monitoring.
```javascript { .api }
/**
* Backup database to file (async operation)
* @param {string} filename - Destination file path
* @param {Object} [options] - Backup options
* @returns {Promise<BackupProgress>} Promise resolving to backup completion info
*/
backup(filename, options);
interface BackupOptions {
attached?: string; // Database name to backup (default: "main")
progress?: Function; // Progress callback function
}
interface BackupProgress {
totalPages: number; // Total pages in database
remainingPages: number; // Pages remaining to backup (0 when complete)
}
```
**Usage Examples:**
```javascript
// Simple backup
await db.backup('backup.db');
console.log('Backup completed');
// Backup with progress monitoring
await db.backup('backup-with-progress.db', {
progress(info) {
const percent = ((info.totalPages - info.remainingPages) / info.totalPages * 100).toFixed(1);
console.log(`Backup progress: ${percent}% (${info.remainingPages} pages remaining)`);
// Return custom page transfer rate (optional)
// return 100; // Transfer 100 pages at a time
}
});
// Backup attached database
db.exec("ATTACH DATABASE 'other.db' AS other");
await db.backup('other-backup.db', { attached: 'other' });
// Backup with error handling
try {
await db.backup('/invalid/path/backup.db');
} catch (error) {
console.error('Backup failed:', error.message);
}
// Throttled backup for large databases
await db.backup('large-backup.db', {
progress(info) {
if (info.remainingPages > 0) {
// Transfer fewer pages at a time to avoid blocking
return 10;
}
}
});
```
### Database Serialization
Serialize database to Buffer for embedding or transmission.
```javascript { .api }
/**
* Serialize database to Buffer
* @param {Object} [options] - Serialization options
* @returns {Buffer} Buffer containing complete serialized database
*/
serialize(options);
interface SerializeOptions {
attached?: string; // Database name to serialize (default: "main")
}
```
**Usage Examples:**
```javascript
// Serialize entire database to buffer
const serialized = db.serialize();
console.log(`Database serialized to ${serialized.length} bytes`);
// Save serialized database to file
const fs = require('fs');
fs.writeFileSync('serialized-db.buf', serialized);
// Restore database from serialized buffer
const restoredDb = new Database(serialized);
// Serialize attached database
db.exec("ATTACH DATABASE 'temp.db' AS temp");
const tempSerialized = db.serialize({ attached: 'temp' });
// Use serialization for database cloning
function cloneDatabase(sourceDb) {
const serialized = sourceDb.serialize();
return new Database(serialized);
}
const clonedDb = cloneDatabase(db);
// Serialization with compression (using external library)
const zlib = require('zlib');
const serialized = db.serialize();
const compressed = zlib.gzipSync(serialized);
console.log(`Compressed from ${serialized.length} to ${compressed.length} bytes`);
// Restore from compressed
const decompressed = zlib.gunzipSync(compressed);
const restoredFromCompressed = new Database(decompressed);
```
### Database Analysis and Introspection
Use PRAGMA commands for comprehensive database analysis.
```javascript { .api }
// Common introspection patterns using pragma()
```
**Schema Information:**
```javascript
// Get all table names
const tables = db.pragma('table_list');
const tableNames = tables.map(table => table.name);
console.log('Tables:', tableNames);
// Get detailed table information
function getTableSchema(tableName) {
const columns = db.pragma(`table_info(${tableName})`);
const indexes = db.pragma(`index_list(${tableName})`);
const foreignKeys = db.pragma(`foreign_key_list(${tableName})`);
return {
columns: columns.map(col => ({
name: col.name,
type: col.type,
nullable: !col.notnull,
defaultValue: col.dflt_value,
primaryKey: !!col.pk
})),
indexes: indexes.map(idx => ({
name: idx.name,
unique: !!idx.unique,
partial: !!idx.partial
})),
foreignKeys: foreignKeys.map(fk => ({
column: fk.from,
referencesTable: fk.table,
referencesColumn: fk.to,
onUpdate: fk.on_update,
onDelete: fk.on_delete
}))
};
}
const userSchema = getTableSchema('users');
console.log(userSchema);
```
**Database Statistics:**
```javascript
// Get database size and page information
const pageCount = db.pragma('page_count', { simple: true });
const pageSize = db.pragma('page_size', { simple: true });
const freePages = db.pragma('freelist_count', { simple: true });
const totalSize = pageCount * pageSize;
const freeSize = freePages * pageSize;
const usedSize = totalSize - freeSize;
console.log(`Database size: ${totalSize} bytes`);
console.log(`Used: ${usedSize} bytes (${(usedSize/totalSize*100).toFixed(1)}%)`);
console.log(`Free: ${freeSize} bytes (${(freeSize/totalSize*100).toFixed(1)}%)`);
// Get compilation options
const compileOptions = db.pragma('compile_options');
console.log('SQLite compile options:', compileOptions);
// Check integrity
const integrityCheck = db.pragma('integrity_check');
if (integrityCheck.length === 1 && integrityCheck[0] === 'ok') {
console.log('Database integrity OK');
} else {
console.warn('Database integrity issues:', integrityCheck);
}
```
**Performance Analysis:**
```javascript
// Analyze query performance
function analyzeQuery(sql) {
const queryPlan = db.pragma(`query_plan(${sql})`);
console.log('Query execution plan:');
queryPlan.forEach(step => {
console.log(`${step.id}: ${step.detail}`);
});
}
analyzeQuery('SELECT * FROM users WHERE email = "test@example.com"');
// Get database statistics
const stats = db.pragma('stats');
stats.forEach(stat => {
console.log(`${stat.table}: ${stat.index} (${stat.cells} cells)`);
});
```
### Database Optimization
Utility functions for database maintenance and optimization.
**Vacuum and Analyze:**
```javascript
// Optimize database storage
function optimizeDatabase() {
console.log('Starting database optimization...');
// Analyze all tables for query planner statistics
db.exec('ANALYZE');
// Rebuild database to reclaim space
db.exec('VACUUM');
console.log('Database optimization completed');
}
// Incremental vacuum (for WAL mode)
function incrementalVacuum(pages = 1000) {
db.pragma(`incremental_vacuum(${pages})`);
}
// Auto-vacuum configuration
db.pragma('auto_vacuum = INCREMENTAL');
```
**Checkpoint Management (WAL Mode):**
```javascript
// Checkpoint WAL file
function checkpoint(mode = 'PASSIVE') {
const result = db.pragma(`wal_checkpoint(${mode})`, { simple: false });
return {
busy: result[0],
log: result[1],
checkpointed: result[2]
};
}
// Different checkpoint modes
const passiveResult = checkpoint('PASSIVE'); // Non-blocking
const fullResult = checkpoint('FULL'); // Block until complete
const restartResult = checkpoint('RESTART'); // Reset WAL file
console.log('Checkpoint results:', fullResult);
```
### Configuration Management
Manage database-wide configuration settings.
```javascript
// Performance tuning
function configureForPerformance() {
db.pragma('journal_mode = WAL'); // Enable WAL mode for better concurrency
db.pragma('synchronous = NORMAL'); // Balance safety and performance
db.pragma('cache_size = 10000'); // Increase cache size
db.pragma('temp_store = MEMORY'); // Use memory for temporary tables
db.pragma('mmap_size = 134217728'); // Enable memory mapping (128MB)
}
// Safety-first configuration
function configureForSafety() {
db.pragma('journal_mode = DELETE'); // Traditional rollback journal
db.pragma('synchronous = FULL'); // Maximum durability
db.pragma('foreign_keys = ON'); // Enforce foreign key constraints
}
// Get current configuration
function getCurrentConfig() {
return {
journalMode: db.pragma('journal_mode', { simple: true }),
synchronous: db.pragma('synchronous', { simple: true }),
cacheSize: db.pragma('cache_size', { simple: true }),
foreignKeys: db.pragma('foreign_keys', { simple: true }),
autoVacuum: db.pragma('auto_vacuum', { simple: true }),
mmapSize: db.pragma('mmap_size', { simple: true })
};
}
console.log('Current configuration:', getCurrentConfig());
```

View file

@ -0,0 +1,280 @@
# better-sqlite3
better-sqlite3 is the fastest and simplest library for SQLite3 in Node.js, providing a synchronous API for high-performance database operations. Unlike the standard node-sqlite3 module which uses callbacks, better-sqlite3 offers direct return values and eliminates callback complexity while maintaining full SQLite feature support.
## Package Information
- **Package Name**: better-sqlite3
- **Package Type**: npm
- **Language**: JavaScript (with TypeScript definitions)
- **Installation**: `npm install better-sqlite3`
## Core Imports
```javascript
const Database = require('better-sqlite3');
```
For TypeScript:
```typescript
import Database from 'better-sqlite3';
// Or for named imports
import Database, { SqliteError } from 'better-sqlite3';
```
## Basic Usage
```javascript
const Database = require('better-sqlite3');
// Create/open database
const db = new Database('mydb.sqlite');
// Create table
db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
// Prepare and execute statements
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertResult = insert.run('John Doe', 'john@example.com');
console.log(insertResult.lastInsertRowid); // 1
// Query data
const getUser = db.prepare('SELECT * FROM users WHERE id = ?');
const user = getUser.get(1);
console.log(user); // { id: 1, name: 'John Doe', email: 'john@example.com' }
// Close database
db.close();
```
## Architecture
better-sqlite3 is built around several key components:
- **Database Class**: Main interface for database connections and management
- **Statement Objects**: Prepared statements for efficient query execution
- **Transaction System**: Built-in transaction management with nested transaction support
- **Type System**: Automatic JavaScript-SQLite type conversions with safe integer support
- **Extension System**: Support for user-defined functions, aggregates, and virtual tables
- **Synchronous API**: Direct return values eliminating callback complexity
## Capabilities
### Database Management
Core database connection and lifecycle management functionality for creating, configuring, and controlling SQLite database connections.
```javascript { .api }
/**
* Creates a new database connection
* @param {string|Buffer} filename - Database file path, ":memory:" for in-memory, or Buffer for serialized database
* @param {Object} [options] - Database configuration options
* @returns {Database} Database instance
*/
function Database(filename, options);
interface DatabaseOptions {
readonly?: boolean; // Open in readonly mode (default: false)
fileMustExist?: boolean; // Throw error if file doesn't exist (default: false)
timeout?: number; // Timeout in ms for locked database (default: 5000)
verbose?: Function; // Function called with every SQL execution
nativeBinding?: string | Object; // Path to native binding or binding object
}
```
[Database Management](./database-management.md)
### Statement Execution
Prepared statement functionality for efficient SQL query execution with parameter binding and result handling.
```javascript { .api }
/**
* Creates a prepared statement from SQL string
* @param {string} sql - SQL query string
* @returns {Statement} Prepared statement object
*/
prepare(sql);
interface Statement {
run(...params): RunResult; // Execute statement, return info
get(...params): Object; // Execute statement, return first row
all(...params): Object[]; // Execute statement, return all rows
iterate(...params): Iterator; // Execute statement, return iterator
bind(...params): Statement; // Permanently bind parameters
readonly reader: boolean; // Whether statement returns data
}
interface RunResult {
changes: number; // Number of rows changed
lastInsertRowid: number; // ID of last inserted row
}
```
[Statement Execution](./statement-execution.md)
### Transaction Management
Transaction system providing ACID compliance with support for nested transactions using savepoints and multiple transaction types.
```javascript { .api }
/**
* Creates a transaction function wrapper
* @param {Function} fn - Function to execute within transaction
* @returns {Function} Transaction function with transaction type variants
*/
transaction(fn);
interface TransactionFunction {
(...args): any; // Default transaction (BEGIN)
deferred(...args): any; // Deferred transaction (BEGIN DEFERRED)
immediate(...args): any; // Immediate transaction (BEGIN IMMEDIATE)
exclusive(...args): any; // Exclusive transaction (BEGIN EXCLUSIVE)
readonly database: Database; // Associated database instance
}
```
[Transaction Management](./transaction-management.md)
### Database Utilities
Utility functions for database introspection, backup, serialization, and configuration management.
```javascript { .api }
/**
* Execute PRAGMA commands for database configuration and introspection
* @param {string} source - PRAGMA command string
* @param {Object} [options] - Execution options
* @returns {Array|any} Results array or single value if simple mode
*/
pragma(source, options);
/**
* Backup database to file
* @param {string} filename - Destination file path
* @param {Object} [options] - Backup options
* @returns {Promise} Promise resolving to backup progress info
*/
backup(filename, options);
/**
* Serialize database to Buffer
* @param {Object} [options] - Serialization options
* @returns {Buffer} Serialized database buffer
*/
serialize(options);
```
[Database Utilities](./database-utilities.md)
### User-Defined Functions
Extension system for creating custom SQL functions, aggregate functions, and virtual tables to extend SQLite functionality.
```javascript { .api }
/**
* Define user-defined function
* @param {string} name - Function name
* @param {Object} [options] - Function options
* @param {Function} fn - JavaScript function implementation
* @returns {Database} Database instance for chaining
*/
function(name, options, fn);
/**
* Define user-defined aggregate function
* @param {string} name - Aggregate function name
* @param {Object} options - Aggregate configuration
* @returns {Database} Database instance for chaining
*/
aggregate(name, options);
/**
* Define virtual table module
* @param {string} name - Module name
* @param {Function|Object} factory - Factory function or table definition
* @returns {Database} Database instance for chaining
*/
table(name, factory);
```
[User-Defined Functions](./user-defined-functions.md)
## Types
```javascript { .api }
class Database {
constructor(filename, options);
// Core methods
prepare(sql): Statement;
transaction(fn): TransactionFunction;
exec(sql): Database;
close(): Database;
// Database utilities
pragma(source, options): Array | any;
backup(filename, options): Promise;
serialize(options): Buffer;
// Extensions
function(name, options, fn): Database;
aggregate(name, options): Database;
table(name, factory): Database;
loadExtension(path, entrypoint): Database;
// Configuration
defaultSafeIntegers(enabled): Database;
unsafeMode(enabled): Database;
// Properties (read-only)
readonly name: string; // Database filename
readonly open: boolean; // Connection status
readonly inTransaction: boolean; // Transaction status
readonly readonly: boolean; // Readonly mode status
readonly memory: boolean; // In-memory database status
}
class Statement {
// Execution methods
run(...params): RunResult;
get(...params): Object | undefined;
all(...params): Object[];
iterate(...params): Iterator;
// Configuration
bind(...params): Statement;
pluck(enabled): Statement;
expand(enabled): Statement;
raw(enabled): Statement;
safeIntegers(enabled): Statement;
columns(): ColumnDescriptor[];
// Properties (read-only)
readonly reader: boolean; // Whether statement returns data
readonly busy: boolean; // Whether statement is executing
readonly source: string; // Original SQL string used to create statement
readonly database: Database; // Associated database instance
}
class SqliteError extends Error {
constructor(message, code);
readonly name: string; // Always "SqliteError"
readonly code: string; // SQLite error code
readonly message: string; // Error message
}
interface RunResult {
changes: number; // Number of rows changed
lastInsertRowid: number; // ID of last inserted row
}
interface ColumnDescriptor {
name: string; // Column name in result set
column: string | null; // Original column name
table: string | null; // Table name
database: string | null; // Database name
type: string | null; // Column data type
}
```

View file

@ -0,0 +1,354 @@
# Statement Execution
Prepared statement functionality for efficient SQL query execution with parameter binding and result handling.
## Capabilities
### Statement Preparation
Create prepared statements from SQL strings for efficient repeated execution.
```javascript { .api }
/**
* Creates a prepared statement from SQL string
* @param {string} sql - SQL query string with optional parameter placeholders
* @returns {Statement} Prepared statement object
*/
prepare(sql);
```
**Usage Examples:**
```javascript
// Prepare INSERT statement
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
// Prepare SELECT statement with named parameters
const getUserByEmail = db.prepare('SELECT * FROM users WHERE email = @email');
// Prepare UPDATE statement with mixed parameters
const updateUser = db.prepare('UPDATE users SET name = ? WHERE id = $id');
// Prepare complex query
const getOrdersWithDetails = db.prepare(`
SELECT o.id, o.total, u.name as customer_name,
COUNT(oi.id) as item_count
FROM orders o
JOIN users u ON o.user_id = u.id
LEFT JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > ?
GROUP BY o.id
ORDER BY o.created_at DESC
`);
```
### Statement Execution Methods
Execute prepared statements and retrieve results in different formats.
```javascript { .api }
/**
* Execute statement for data modification, return info about changes
* @param {...any} params - Parameters to bind to statement
* @returns {RunResult} Object with changes and lastInsertRowid
*/
run(...params);
/**
* Execute statement and return first row
* @param {...any} params - Parameters to bind to statement
* @returns {Object|undefined} First row object or undefined if no results
*/
get(...params);
/**
* Execute statement and return all rows
* @param {...any} params - Parameters to bind to statement
* @returns {Object[]} Array of row objects
*/
all(...params);
/**
* Execute statement and return iterator for memory-efficient processing
* @param {...any} params - Parameters to bind to statement
* @returns {Iterator<Object>} Iterator yielding row objects
*/
iterate(...params);
interface RunResult {
changes: number; // Number of rows changed by the operation
lastInsertRowid: number; // Row ID of last inserted row (0 if none)
}
```
**Usage Examples:**
```javascript
// Using run() for data modification
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const result = insertUser.run('Alice Smith', 'alice@example.com');
console.log(result.changes); // 1
console.log(result.lastInsertRowid); // 1 (the new user's ID)
// Using get() for single row retrieval
const getUser = db.prepare('SELECT * FROM users WHERE id = ?');
const user = getUser.get(1);
console.log(user); // { id: 1, name: 'Alice Smith', email: 'alice@example.com' }
// Using all() for multiple rows
const getAllUsers = db.prepare('SELECT * FROM users ORDER BY name');
const users = getAllUsers.all();
console.log(users.length); // Number of users
users.forEach(user => console.log(user.name));
// Using iterate() for memory-efficient processing of large result sets
const getActiveOrders = db.prepare('SELECT * FROM orders WHERE status = ?');
for (const order of getActiveOrders.iterate('active')) {
console.log(`Order ${order.id}: $${order.total}`);
// Process one order at a time without loading all into memory
}
```
### Parameter Binding
Bind parameters to prepared statements for secure and efficient execution.
```javascript { .api }
/**
* Permanently bind parameters to statement
* @param {...any} params - Parameters to bind (positional or named)
* @returns {Statement} Statement instance for chaining
*/
bind(...params);
```
**Parameter Binding Styles:**
```javascript
// Positional parameters with ?
const stmt1 = db.prepare('SELECT * FROM users WHERE age > ? AND city = ?');
stmt1.run(25, 'New York'); // Temporary binding
stmt1.bind(25, 'New York'); // Permanent binding
// Named parameters with @name, :name, or $name
const stmt2 = db.prepare('SELECT * FROM users WHERE age > @minAge AND city = @city');
stmt2.run({ minAge: 25, city: 'New York' });
// Object binding for named parameters
const stmt3 = db.prepare('INSERT INTO users (name, email, age) VALUES (@name, @email, @age)');
stmt3.run({
name: 'Bob Wilson',
email: 'bob@example.com',
age: 30
});
// Array binding for positional parameters
const stmt4 = db.prepare('INSERT INTO users (name, email, age) VALUES (?, ?, ?)');
stmt4.run(['Charlie Brown', 'charlie@example.com', 28]);
// Permanent binding prevents further parameter passing
const boundStmt = db.prepare('SELECT * FROM users WHERE status = ?');
boundStmt.bind('active');
// boundStmt.run('inactive'); // Would throw TypeError
```
### Statement Configuration
Configure statement behavior for result formatting and data handling.
```javascript { .api }
/**
* Enable/disable column plucking (return only first column value)
* @param {boolean} enabled - Enable plucking mode
* @returns {Statement} Statement instance for chaining
*/
pluck(enabled);
/**
* Enable/disable row expansion (return nested objects by table)
* @param {boolean} enabled - Enable expansion mode
* @returns {Statement} Statement instance for chaining
*/
expand(enabled);
/**
* Enable/disable raw mode (return arrays instead of objects)
* @param {boolean} enabled - Enable raw mode
* @returns {Statement} Statement instance for chaining
*/
raw(enabled);
/**
* Enable/disable safe integer mode for this statement
* @param {boolean} enabled - Enable safe integers (return BigInt for large numbers)
* @returns {Statement} Statement instance for chaining
*/
safeIntegers(enabled);
```
**Usage Examples:**
```javascript
// Pluck mode - return only first column value
const getName = db.prepare('SELECT name FROM users WHERE id = ?').pluck();
const name = getName.get(1); // Returns "Alice Smith" instead of { name: "Alice Smith" }
// Raw mode - return arrays instead of objects
const getUserRaw = db.prepare('SELECT id, name, email FROM users WHERE id = ?').raw();
const userData = getUserRaw.get(1); // Returns [1, "Alice Smith", "alice@example.com"]
// Expand mode - nested objects by table
const getOrderWithUser = db.prepare(`
SELECT o.id, o.total, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.id = ?
`).expand();
const result = getOrderWithUser.get(1);
// Returns: { orders: { id: 1, total: 99.99 }, users: { name: "Alice", email: "alice@example.com" } }
// Safe integers for handling large numbers
const getBigNumber = db.prepare('SELECT big_integer_column FROM table WHERE id = ?').safeIntegers();
const bigNum = getBigNumber.get(1); // Returns BigInt instead of potentially unsafe number
// Chain configuration methods
const configuredStmt = db.prepare('SELECT COUNT(*) as count FROM users')
.pluck() // Only return the count value
.safeIntegers(true); // Use BigInt for large counts
const userCount = configuredStmt.get(); // Returns BigInt directly
```
### Statement Metadata
Retrieve metadata information about prepared statements.
```javascript { .api }
/**
* Get column information for SELECT statements
* @returns {ColumnDescriptor[]} Array of column descriptors
*/
columns();
interface ColumnDescriptor {
name: string; // Column name in result set
column: string | null; // Original column name (null for expressions)
table: string | null; // Source table name (null for expressions)
database: string | null; // Database name (typically "main")
type: string | null; // Declared column type (null for expressions)
}
```
**Usage Examples:**
```javascript
// Get column metadata
const stmt = db.prepare('SELECT u.id, u.name, COUNT(*) as order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id');
const columns = stmt.columns();
columns.forEach(col => {
console.log(`Column: ${col.name}`);
console.log(` Original: ${col.column}`);
console.log(` Table: ${col.table}`);
console.log(` Type: ${col.type}`);
});
// Output:
// Column: id
// Original: id
// Table: users
// Type: INTEGER
// Column: name
// Original: name
// Table: users
// Type: TEXT
// Column: order_count
// Original: null
// Table: null
// Type: null
```
### Statement Properties
Read-only properties providing statement information.
```javascript { .api }
interface StatementProperties {
readonly reader: boolean; // Whether statement returns data (SELECT vs INSERT/UPDATE/etc)
readonly busy: boolean; // Whether statement is currently executing
readonly database: Database; // Associated database instance
}
```
**Usage Examples:**
```javascript
const selectStmt = db.prepare('SELECT * FROM users');
const insertStmt = db.prepare('INSERT INTO users (name) VALUES (?)');
console.log(selectStmt.reader); // true (SELECT statement)
console.log(insertStmt.reader); // false (INSERT statement)
// Can't use get/all/iterate on non-reader statements
try {
insertStmt.get(); // Throws TypeError
} catch (error) {
console.log(error.message); // "This statement does not return data"
}
// Can't use columns() on non-reader statements
try {
insertStmt.columns(); // Throws TypeError
} catch (error) {
console.log(error.message); // "This statement does not return data"
}
console.log(selectStmt.database === db); // true
console.log(selectStmt.busy); // false (when not executing)
```
## Data Type Conversions
better-sqlite3 automatically converts between JavaScript and SQLite data types:
**JavaScript to SQLite:**
- `null`, `undefined` → NULL
- `string` → TEXT
- `number` → INTEGER or REAL
- `bigint` → INTEGER (when safe integers enabled)
- `boolean` → INTEGER (0 or 1)
- `Buffer` → BLOB
- `Date` → TEXT (ISO string) or INTEGER (timestamp)
**SQLite to JavaScript:**
- NULL → `null`
- INTEGER → `number` or `bigint` (when safe integers enabled)
- REAL → `number`
- TEXT → `string`
- BLOB → `Buffer`
**Usage Examples:**
```javascript
// Inserting different data types
const insertData = db.prepare('INSERT INTO mixed_data (text_col, int_col, real_col, blob_col, bool_col) VALUES (?, ?, ?, ?, ?)');
insertData.run(
'Hello World', // TEXT
42, // INTEGER
3.14159, // REAL
Buffer.from('binary data'), // BLOB
true // INTEGER (1)
);
// Date handling
const insertDate = db.prepare('INSERT INTO events (name, created_at) VALUES (?, ?)');
insertDate.run('Event Name', new Date()); // Date becomes TEXT ISO string
// Large integer handling with safe integers
db.defaultSafeIntegers(true);
const insertBigInt = db.prepare('INSERT INTO big_numbers (value) VALUES (?)');
insertBigInt.run(9007199254740992n); // BigInt preserved as INTEGER
const getBigInt = db.prepare('SELECT value FROM big_numbers WHERE id = ?');
const result = getBigInt.get(1);
console.log(typeof result.value); // "bigint"
```

View file

@ -0,0 +1,318 @@
# Transaction Management
Transaction system providing ACID compliance with support for nested transactions using savepoints and multiple transaction types.
## Capabilities
### Transaction Creation
Create transaction wrapper functions that automatically handle BEGIN/COMMIT/ROLLBACK operations.
```javascript { .api }
/**
* Creates a transaction function wrapper
* @param {Function} fn - Function to execute within transaction
* @returns {TransactionFunction} Transaction function with transaction type variants
*/
transaction(fn);
interface TransactionFunction {
(...args): any; // Default transaction using BEGIN
deferred(...args): any; // Deferred transaction using BEGIN DEFERRED
immediate(...args): any; // Immediate transaction using BEGIN IMMEDIATE
exclusive(...args): any; // Exclusive transaction using BEGIN EXCLUSIVE
readonly database: Database; // Associated database instance
}
```
**Usage Examples:**
```javascript
// Create transaction function
const insertMany = db.transaction((users) => {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (@name, @email)');
for (const user of users) {
insert.run(user);
}
});
// Execute transaction
insertMany([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' }
]);
// All users inserted atomically, or none if any fails
```
### Transaction Types
Different transaction types provide various levels of locking and concurrency control.
```javascript { .api }
// Transaction type variants
transactionFunction(); // BEGIN (same as deferred)
transactionFunction.deferred(); // BEGIN DEFERRED (default SQLite behavior)
transactionFunction.immediate(); // BEGIN IMMEDIATE (acquire reserved lock immediately)
transactionFunction.exclusive(); // BEGIN EXCLUSIVE (acquire exclusive lock immediately)
```
**Transaction Type Behaviors:**
```javascript
const updateInventory = db.transaction((items) => {
const update = db.prepare('UPDATE inventory SET quantity = quantity - ? WHERE item_id = ?');
for (const item of items) {
update.run(item.quantity, item.id);
}
});
// Deferred transaction - acquire locks as needed (default)
updateInventory.deferred(items);
// Immediate transaction - acquire reserved lock immediately
// (prevents other writers from starting)
updateInventory.immediate(items);
// Exclusive transaction - acquire exclusive lock immediately
// (prevents all other connections from reading or writing)
updateInventory.exclusive(items);
```
### Nested Transactions
Transactions can be nested using SQLite savepoints for complex error handling scenarios.
```javascript { .api }
// Nested transactions automatically use savepoints
// Inner transaction failures roll back to savepoint, not beginning
```
**Usage Examples:**
```javascript
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertProfile = db.prepare('INSERT INTO profiles (user_id, bio) VALUES (?, ?)');
const insertPreferences = db.prepare('INSERT INTO preferences (user_id, theme) VALUES (?, ?)');
// Outer transaction
const createUserWithProfile = db.transaction((userData) => {
// Insert user
const userResult = insertUser.run(userData.name, userData.email);
const userId = userResult.lastInsertRowid;
// Inner transaction for profile creation
const createProfile = db.transaction((profileData) => {
insertProfile.run(userId, profileData.bio);
if (profileData.preferences) {
// Nested inner transaction for preferences
const setPreferences = db.transaction((prefs) => {
insertPreferences.run(userId, prefs.theme);
if (prefs.theme === 'invalid') {
throw new Error('Invalid theme'); // This will rollback only preferences
}
});
setPreferences(profileData.preferences);
}
});
try {
createProfile(userData.profile);
} catch (error) {
console.log('Profile creation failed, but user was created');
// User insert is preserved, only profile operations rolled back
}
});
// Execute the nested transaction
createUserWithProfile({
name: 'John Doe',
email: 'john@example.com',
profile: {
bio: 'Software developer',
preferences: { theme: 'dark' }
}
});
```
### Transaction Error Handling
Transactions automatically handle errors with proper rollback behavior.
```javascript { .api }
// Automatic rollback on any thrown error
// Error propagates normally after rollback
// Nested transactions use savepoints for partial rollback
```
**Usage Examples:**
```javascript
const transferFunds = db.transaction((fromAccount, toAccount, amount) => {
const debit = db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?');
const credit = db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?');
const getBalance = db.prepare('SELECT balance FROM accounts WHERE id = ?');
// Check sufficient funds
const fromBalance = getBalance.get(fromAccount).balance;
if (fromBalance < amount) {
throw new Error('Insufficient funds'); // Transaction will rollback
}
// Perform transfer
debit.run(amount, fromAccount);
credit.run(amount, toAccount);
// Verify the transfer
const newFromBalance = getBalance.get(fromAccount).balance;
const newToBalance = getBalance.get(toAccount).balance;
return {
fromBalance: newFromBalance,
toBalance: newToBalance,
transferred: amount
};
});
try {
const result = transferFunds(1, 2, 100);
console.log('Transfer completed:', result);
} catch (error) {
console.log('Transfer failed:', error.message);
// Database state unchanged due to automatic rollback
}
```
### Transaction Function Properties
Transaction functions expose properties for introspection and database access.
```javascript { .api }
interface TransactionFunction {
readonly database: Database; // Associated database instance
// All transaction type variants (deferred, immediate, exclusive) have same properties
}
```
**Usage Examples:**
```javascript
const batchInsert = db.transaction((data) => {
// Function implementation
});
// Access the associated database
console.log(batchInsert.database === db); // true
// All transaction variants share the same database reference
console.log(batchInsert.deferred.database === db); // true
console.log(batchInsert.immediate.database === db); // true
console.log(batchInsert.exclusive.database === db); // true
// Check if currently in transaction during execution
const checkTransactionStatus = db.transaction(() => {
console.log(db.inTransaction); // true during transaction execution
});
checkTransactionStatus();
console.log(db.inTransaction); // false after transaction completes
```
### Transaction Best Practices
Guidelines for effective transaction usage and performance optimization.
**Performance Considerations:**
```javascript
// Good: Batch multiple operations in a single transaction
const batchUserInsert = db.transaction((users) => {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
for (const user of users) {
insert.run(user.name, user.email);
}
});
// Bad: Individual transactions for each operation (slow)
function insertUsersSlow(users) {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
for (const user of users) {
const singleInsert = db.transaction(() => {
insert.run(user.name, user.email);
});
singleInsert(); // Creates transaction overhead for each insert
}
}
// Good: Short-lived transactions
const quickUpdate = db.transaction((id, newValue) => {
db.prepare('UPDATE table SET value = ? WHERE id = ?').run(newValue, id);
});
// Avoid: Long-running transactions that hold locks
const avoidLongTransaction = db.transaction(() => {
// Don't do this - holds database locks too long
for (let i = 0; i < 1000000; i++) {
// Long-running computation
heavyComputation();
}
db.prepare('INSERT INTO results VALUES (?)').run(result);
});
```
**Error Handling Patterns:**
```javascript
// Pattern: Specific error handling with graceful degradation
const safeUserCreation = db.transaction((userData) => {
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertAuditLog = db.prepare('INSERT INTO audit_log (action, details) VALUES (?, ?)');
try {
const result = insertUser.run(userData.name, userData.email);
// Nested transaction for audit log (can fail without affecting user creation)
const logAudit = db.transaction(() => {
insertAuditLog.run('user_created', JSON.stringify(userData));
});
try {
logAudit();
} catch (auditError) {
console.warn('Audit logging failed:', auditError.message);
// User creation still succeeds
}
return result.lastInsertRowid;
} catch (error) {
console.error('User creation failed:', error.message);
throw error; // Re-throw to trigger rollback
}
});
```
### Manual Transaction Control
While transaction functions are recommended, manual transaction control is also possible.
```javascript
// Manual transaction control (not recommended with transaction functions)
db.exec('BEGIN');
try {
db.prepare('INSERT INTO users (name) VALUES (?)').run('John');
db.prepare('INSERT INTO profiles (user_id) VALUES (?)').run(1);
db.exec('COMMIT');
} catch (error) {
db.exec('ROLLBACK');
throw error;
}
// Warning: Don't mix manual control with transaction functions
const mixedApproach = db.transaction(() => {
// Don't use raw COMMIT/ROLLBACK inside transaction functions
// db.exec('COMMIT'); // This will cause issues
});
```

View file

@ -0,0 +1,410 @@
# User-Defined Functions
Extension system for creating custom SQL functions, aggregate functions, and virtual tables to extend SQLite functionality.
## Capabilities
### User-Defined Functions
Create custom SQL functions that can be called from within SQL queries.
```javascript { .api }
/**
* Define user-defined function
* @param {string} name - Function name (used in SQL queries)
* @param {Object} [options] - Function configuration options
* @param {Function} fn - JavaScript function implementation
* @returns {Database} Database instance for chaining
*/
function(name, options, fn);
interface FunctionOptions {
safeIntegers?: boolean; // Use safe integers for this function (default: database setting)
deterministic?: boolean; // Function is deterministic (default: false)
directOnly?: boolean; // Function can only be called directly, not in triggers/views (default: false)
varargs?: boolean; // Function accepts variable arguments (default: false)
}
```
**Usage Examples:**
```javascript
// Simple string function
db.function('reverse', (str) => {
return typeof str === 'string' ? str.split('').reverse().join('') : null;
});
// Use in SQL query
const result = db.prepare('SELECT reverse(name) as reversed_name FROM users').all();
console.log(result); // [{ reversed_name: 'ecilA' }, { reversed_name: 'boB' }, ...]
// Math function with multiple parameters
db.function('pythagoras', (a, b) => {
if (typeof a !== 'number' || typeof b !== 'number') return null;
return Math.sqrt(a * a + b * b);
});
const distance = db.prepare('SELECT pythagoras(3, 4) as distance').get();
console.log(distance.distance); // 5
// Function with options
db.function('random_int', {
deterministic: false, // Result can vary between calls
directOnly: true // Can't be used in triggers or views
}, (min, max) => {
if (typeof min !== 'number' || typeof max !== 'number') return null;
return Math.floor(Math.random() * (max - min + 1)) + min;
});
// Variable arguments function
db.function('concat_with_separator', {
varargs: true
}, (separator, ...args) => {
if (typeof separator !== 'string') return null;
return args.filter(arg => arg != null).join(separator);
});
const concatenated = db.prepare("SELECT concat_with_separator(' | ', name, email, city) as info FROM users").all();
```
### Aggregate Functions
Create custom aggregate functions for GROUP BY operations and window functions.
```javascript { .api }
/**
* Define user-defined aggregate function
* @param {string} name - Aggregate function name
* @param {Object} options - Aggregate configuration (required)
* @returns {Database} Database instance for chaining
*/
aggregate(name, options);
interface AggregateOptions {
start?: any; // Initial accumulator value (default: null)
step: Function; // Step function called for each row (required)
inverse?: Function; // Inverse function for window functions (optional)
result?: Function; // Final result transformation function (optional)
safeIntegers?: boolean; // Use safe integers (default: database setting)
deterministic?: boolean; // Function is deterministic (default: false)
directOnly?: boolean; // Function can only be called directly (default: false)
varargs?: boolean; // Function accepts variable arguments (default: false)
}
```
**Usage Examples:**
```javascript
// String concatenation aggregate
db.aggregate('group_concat_custom', {
start: '',
step: (accumulator, value) => {
if (value == null) return accumulator;
return accumulator === '' ? String(value) : accumulator + ', ' + String(value);
},
result: (accumulator) => accumulator || null
});
const groupedNames = db.prepare('SELECT group_concat_custom(name) as names FROM users').get();
console.log(groupedNames.names); // "Alice, Bob, Charlie"
// Mathematical aggregate - geometric mean
db.aggregate('geometric_mean', {
start: { product: 1, count: 0 },
step: (accumulator, value) => {
if (typeof value !== 'number' || value <= 0) return accumulator;
return {
product: accumulator.product * value,
count: accumulator.count + 1
};
},
result: (accumulator) => {
if (accumulator.count === 0) return null;
return Math.pow(accumulator.product, 1 / accumulator.count);
}
});
const geometricMean = db.prepare('SELECT geometric_mean(value) as geo_mean FROM measurements').get();
// Window function with inverse (for sliding windows)
db.aggregate('running_average', {
start: { sum: 0, count: 0 },
step: (accumulator, value) => {
if (typeof value !== 'number') return accumulator;
return {
sum: accumulator.sum + value,
count: accumulator.count + 1
};
},
inverse: (accumulator, value) => {
if (typeof value !== 'number') return accumulator;
return {
sum: accumulator.sum - value,
count: accumulator.count - 1
};
},
result: (accumulator) => {
return accumulator.count > 0 ? accumulator.sum / accumulator.count : null;
}
});
// Use as window function
const runningAverage = db.prepare(`
SELECT value,
running_average(value) OVER (ORDER BY id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as avg_3
FROM measurements
ORDER BY id
`).all();
// Complex aggregate with multiple parameters
db.aggregate('weighted_average', {
start: { weightedSum: 0, totalWeight: 0 },
step: (accumulator, value, weight) => {
if (typeof value !== 'number' || typeof weight !== 'number' || weight <= 0) {
return accumulator;
}
return {
weightedSum: accumulator.weightedSum + (value * weight),
totalWeight: accumulator.totalWeight + weight
};
},
result: (accumulator) => {
return accumulator.totalWeight > 0 ? accumulator.weightedSum / accumulator.totalWeight : null;
}
});
```
### Virtual Tables
Create virtual tables that generate data dynamically or interface with external data sources.
```javascript { .api }
/**
* Define virtual table module
* @param {string} name - Module name
* @param {Function|Object} factory - Factory function or eponymous table definition
* @returns {Database} Database instance for chaining
*/
table(name, factory);
// Factory function signature
interface TableFactory {
(moduleName: string, databaseName: string, tableName: string, ...args): TableDefinition;
}
// Table definition object
interface TableDefinition {
columns: string[]; // Column names (required)
rows: GeneratorFunction; // Generator function yielding rows (required)
parameters?: string[]; // Parameter names (optional, inferred from rows.length)
safeIntegers?: boolean; // Use safe integers (default: database setting)
directOnly?: boolean; // Table can only be accessed directly (default: false)
}
```
**Usage Examples:**
```javascript
// Simple eponymous-only virtual table (no factory)
db.table('fibonacci', {
columns: ['value', 'position'],
*rows(limit = 10) {
let a = 0, b = 1, position = 0;
while (position < limit) {
yield [a, position];
[a, b] = [b, a + b];
position++;
}
}
});
// Use the virtual table
const fibNumbers = db.prepare('SELECT * FROM fibonacci(15)').all();
console.log(fibNumbers); // First 15 Fibonacci numbers
// Factory-based virtual table for flexibility
db.table('sequence_generator', function(moduleName, databaseName, tableName, type) {
return {
columns: ['value', 'index'],
parameters: ['start', 'end', 'step'],
*rows(start, end, step = 1) {
let index = 0;
for (let value = start; value <= end; value += step) {
if (type === 'even' && value % 2 !== 0) continue;
if (type === 'odd' && value % 2 === 0) continue;
yield { value, index: index++ };
}
}
};
});
// Create specific table instances
db.exec('CREATE VIRTUAL TABLE even_numbers USING sequence_generator(even)');
db.exec('CREATE VIRTUAL TABLE odd_numbers USING sequence_generator(odd)');
// Query the virtual tables
const evenNumbers = db.prepare('SELECT * FROM even_numbers(2, 20, 2)').all();
const oddNumbers = db.prepare('SELECT * FROM odd_numbers(1, 19, 2)').all();
// Complex virtual table with external data
db.table('file_system', {
columns: ['name', 'size', 'type', 'modified'],
parameters: ['directory'],
*rows(directory = '.') {
const fs = require('fs');
const path = require('path');
try {
const entries = fs.readdirSync(directory, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(directory, entry.name);
const stats = fs.statSync(fullPath);
yield {
name: entry.name,
size: stats.size,
type: entry.isDirectory() ? 'directory' : 'file',
modified: stats.mtime.toISOString()
};
}
} catch (error) {
// Handle directory access errors gracefully
console.warn(`Cannot read directory ${directory}:`, error.message);
}
}
});
// Query file system
const files = db.prepare(`
SELECT name, size, type
FROM file_system('/usr/local/bin')
WHERE type = 'file' AND size > 1000
ORDER BY size DESC
`).all();
// Virtual table with JSON data processing
db.table('json_parser', {
columns: ['key', 'value', 'type'],
parameters: ['json_string', 'path'],
*rows(jsonString, path = '$') {
try {
const data = JSON.parse(jsonString);
function* processObject(obj, currentPath) {
for (const [key, value] of Object.entries(obj)) {
const fullPath = currentPath === '$' ? `$.${key}` : `${currentPath}.${key}`;
if (path === '$' || fullPath.startsWith(path)) {
yield {
key: fullPath,
value: typeof value === 'object' ? JSON.stringify(value) : String(value),
type: Array.isArray(value) ? 'array' : typeof value
};
}
if (typeof value === 'object' && value !== null) {
yield* processObject(value, fullPath);
}
}
}
yield* processObject(data, '$');
} catch (error) {
yield { key: 'error', value: error.message, type: 'error' };
}
}
});
// Parse JSON data
const jsonData = '{"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]}';
const parsedData = db.prepare(`
SELECT key, value, type
FROM json_parser(?, '$.users')
WHERE type != 'object'
`).all(jsonData);
```
### Function and Table Management
Utilities for managing user-defined functions and virtual tables.
**Function Replacement:**
```javascript
// Functions can be replaced by redefining
db.function('my_function', () => 'version 1');
let result1 = db.prepare('SELECT my_function() as result').get();
console.log(result1.result); // "version 1"
db.function('my_function', () => 'version 2');
let result2 = db.prepare('SELECT my_function() as result').get();
console.log(result2.result); // "version 2"
// Remove function by providing null
db.function('my_function', null);
// Now my_function() is no longer available
```
**Error Handling in Functions:**
```javascript
// Handle errors gracefully in user-defined functions
db.function('safe_divide', (a, b) => {
if (typeof a !== 'number' || typeof b !== 'number') return null;
if (b === 0) return null; // Return null for division by zero
return a / b;
});
// Function that throws errors
db.function('strict_divide', (a, b) => {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Arguments must be numbers');
}
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
});
// Errors in functions become SQL errors
try {
db.prepare('SELECT strict_divide(10, 0)').get();
} catch (error) {
console.log(error.message); // "Division by zero"
}
```
**Performance Considerations:**
```javascript
// Use deterministic flag for cacheable functions
db.function('expensive_calculation', {
deterministic: true // SQLite can cache results
}, (input) => {
// Expensive computation that always returns same result for same input
return heavyMathOperation(input);
});
// Use directOnly for security-sensitive functions
db.function('get_secret', {
directOnly: true // Prevents use in triggers, views, or stored procedures
}, () => {
return process.env.SECRET_KEY;
});
// Optimize virtual tables for large datasets
db.table('large_dataset', {
columns: ['id', 'data'],
*rows(limit = 1000000) {
for (let i = 0; i < limit; i++) {
// Yield data incrementally to avoid memory issues
yield { id: i, data: `record_${i}` };
// Optional: yield control periodically for very large datasets
if (i % 10000 === 0) {
setImmediate(() => {}); // Allow event loop to process other tasks
}
}
}
});
```

View file

@ -0,0 +1,7 @@
{
"name": "tessl/npm-better-sqlite3",
"version": "12.2.0",
"docs": "docs/index.md",
"describes": "pkg:npm/better-sqlite3@12.2.0",
"summary": "The fastest and simplest library for SQLite3 in Node.js with synchronous API for high-performance database operations"
}

View file

@ -0,0 +1,407 @@
# CLI Usage
Jest provides a comprehensive command-line interface with over 70 options for controlling test execution, coverage collection, watch mode, output formatting, and project configuration.
## Basic CLI Usage
Jest can be run from the command line with various options:
```bash
# Run all tests
jest
# Run tests matching a pattern
jest MyComponent
# Run tests in a specific directory
jest src/components
# Run tests with coverage
jest --coverage
# Run tests in watch mode
jest --watch
```
## CLI Options Reference
### Test Execution Options
Control how tests are discovered, executed, and filtered.
```bash
# Stop after N test failures
jest --bail
jest --bail=3
# Find tests related to specified files
jest --findRelatedTests src/utils.js src/components/Button.js
# List all tests Jest will run without executing them
jest --listTests
# Only run tests related to changed files (requires git)
jest --onlyChanged
jest -o
# Run only tests that failed in previous execution
jest --onlyFailures
jest -f
# Don't fail when no tests are found
jest --passWithNoTests
# Run tests serially in current process instead of parallel workers
jest --runInBand
jest -i
# Use exact file paths instead of patterns
jest --runTestsByPath path/to/test1.js path/to/test2.js
# Run tests matching name pattern (regex)
jest --testNamePattern="should add"
jest -t "user login"
# Regexp patterns for test file paths
jest --testPathPatterns="__tests__.*\.js$"
# Override testRegex configuration
jest --testRegex=".*\.(test|spec)\.(js|ts)$"
# Set default test timeout in milliseconds
jest --testTimeout=10000
```
### Watch Mode Options
Configure file watching and automatic test re-execution.
```bash
# Watch files and rerun related tests on changes
jest --watch
# Watch files and rerun all tests on changes
jest --watchAll
# Ignore patterns for watch mode
jest --watchPathIgnorePatterns="node_modules" --watchPathIgnorePatterns="build"
```
## Testing Options
### Test Selection and Filtering
```bash
# Run only tests that failed in the previous run
jest --onlyFailures
# Run only tests related to changed files (requires git)
jest --onlyChanged
# Run tests related to specific files
jest --findRelatedTests src/utils.js src/components/Button.js
# Run tests matching a name pattern
jest --testNamePattern="should render correctly"
# List all tests Jest would run without executing them
jest --listTests
# Run tests by exact file paths instead of patterns
jest --runTestsByPath
```
### Test Execution Control
```bash
# Stop after N test failures
jest --bail
jest --bail=3
# Run tests serially in the current process
jest --runInBand
# Set maximum number of worker processes
jest --maxWorkers=4
jest --maxWorkers=50%
# Set default timeout for tests
jest --testTimeout=10000
# Don't fail when no tests are found
jest --passWithNoTests
```
## Coverage Options
### Basic Coverage
```bash
# Collect and report test coverage
jest --coverage
jest --collectCoverage # equivalent
# Specify coverage output directory
jest --coverageDirectory=coverage-report
# Choose coverage provider
jest --coverageProvider=babel
jest --coverageProvider=v8
```
### Coverage Configuration
```bash
# Collect coverage from specific files
jest --collectCoverageFrom="src/**/*.js" --collectCoverageFrom="!src/**/*.test.js"
# Ignore patterns for coverage
jest --coveragePathIgnorePatterns=node_modules --coveragePathIgnorePatterns=build
# Specify coverage reporters
jest --coverageReporters=text --coverageReporters=html --coverageReporters=lcov
# Set coverage thresholds (will fail if not met)
jest --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'
```
## Output and Reporting
### Output Formats
```bash
# Output results in JSON format
jest --json
# Write JSON output to a file
jest --json --outputFile=test-results.json
# Verbose output showing individual test results
jest --verbose
# Silent mode - prevent tests from printing to console
jest --silent
# Disable stack traces in test output
jest --noStackTrace
# Force colored output
jest --colors
```
### Advanced Output Control
```bash
# Log heap usage after each test
jest --logHeapUsage
# Detect and report memory leaks (experimental)
jest --detectLeaks
# Detect handles that prevent Jest from exiting
jest --detectOpenHandles
# Force Jest to exit after tests complete
jest --forceExit
```
## Configuration Options
### Configuration Sources
```bash
# Use specific config file
jest --config=jest.config.js
jest --config=package.json
# Provide configuration as JSON string
jest --config='{"testEnvironment":"node"}'
# Set root directory
jest --rootDir=/path/to/project
# Specify multiple root directories
jest --roots=src --roots=lib
# Run tests for multiple projects
jest --projects=project1 --projects=project2
```
### Environment and Setup
```bash
# Specify test environment
jest --testEnvironment=node
jest --testEnvironment=jsdom
# Clear all mocks before each test
jest --clearMocks
# Reset module registry before each test
jest --resetModules
# Restore mocks after each test
jest --restoreMocks
```
## Cache Management
```bash
# Use the transform cache (default: true)
jest --cache
# Disable the transform cache
jest --no-cache
# Specify cache directory
jest --cacheDirectory=/tmp/jest-cache
# Clear cache and exit (useful for CI)
jest --clearCache
```
## Advanced Options
### Development and Debugging
```bash
# Enable debug mode
jest --debug
# Throw error on deprecated API usage
jest --errorOnDeprecated
# Update snapshots
jest --updateSnapshot
# Randomize test order within files
jest --randomize
# Set seed for test randomization
jest --seed=12345
```
### Performance and Concurrency
```bash
# Set maximum concurrent tests when using test.concurrent
jest --maxConcurrency=5
# Use specific number of workers
jest --maxWorkers=2
# Run tests in band (single process) for debugging
jest -i # short for --runInBand
```
## Watch Mode Patterns
When running in watch mode (`--watch` or `--watchAll`), Jest provides an interactive interface:
```bash
# Watch mode commands (available during watch mode):
# Press 'a' to run all tests
# Press 'f' to run only failed tests
# Press 'o' to only run tests related to changed files
# Press 'p' to filter by a filename regex pattern
# Press 't' to filter by a test name regex pattern
# Press 'q' to quit watch mode
# Press 'Enter' to trigger a test run
```
### Watch Mode Configuration
```bash
# Ignore patterns in watch mode
jest --watch --watchPathIgnorePatterns=node_modules --watchPathIgnorePatterns=build
# Watch all files (not just tracked by git)
jest --watchAll
# Combine with other options
jest --watch --coverage --verbose
```
## CLI Argument Building Programmatically
```typescript { .api }
import { buildArgv } from 'jest';
function buildArgv(maybeArgv?: Array<string>): Promise<Config.Argv>
```
You can build CLI arguments programmatically using the `buildArgv` function:
```typescript
import { buildArgv, runCLI } from 'jest';
// Build arguments for CI environment
const ciArgv = await buildArgv([
'--ci',
'--coverage',
'--json',
'--runInBand',
'--passWithNoTests',
'--outputFile=test-results.json'
]);
// Build arguments for development
const devArgv = await buildArgv([
'--watch',
'--verbose',
'--testPathPatterns=src/'
]);
// Build arguments with coverage thresholds
const strictArgv = await buildArgv([
'--coverage',
'--coverageThreshold={"global":{"branches":90,"functions":90,"lines":90,"statements":90}}'
]);
```
## Common CLI Workflows
### Continuous Integration
```bash
# Typical CI command
jest --ci --coverage --json --runInBand --passWithNoTests --outputFile=results.json
# With coverage enforcement
jest --ci --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'
```
### Development Workflow
```bash
# Start development with watch mode
jest --watch --verbose
# Run tests for specific feature
jest --watch --testPathPatterns=src/features/auth
# Debug failing tests
jest --runInBand --verbose --testNamePattern="failing test name"
```
### Pre-commit Checks
```bash
# Run only tests related to staged files
jest --onlyChanged --passWithNoTests
# Full validation with coverage
jest --coverage --bail=1
```
### Performance Analysis
```bash
# Analyze test performance
jest --verbose --logHeapUsage --detectLeaks
# Profile with single worker for consistency
jest --runInBand --logHeapUsage
```
The Jest CLI provides comprehensive control over test execution, making it suitable for development, continuous integration, and automated testing workflows.

View file

@ -0,0 +1,513 @@
# Configuration
Jest provides a comprehensive configuration system supporting global settings, per-project configuration, and extensive customization options for all aspects of test execution.
## Capabilities
### Core Configuration Types
Jest uses a hierarchical configuration system with different types serving specific purposes.
```typescript { .api }
/**
* Main user configuration interface (alias for Config.InitialOptions)
* This is what users provide in jest.config.js or package.json
*/
interface Config {
// Test discovery
testMatch?: Array<string>;
testRegex?: string | Array<string>;
testPathIgnorePatterns?: Array<string>;
// Test environment
testEnvironment?: string;
testEnvironmentOptions?: Record<string, any>;
// Module resolution
moduleDirectories?: Array<string>;
moduleFileExtensions?: Array<string>;
moduleNameMapper?: Record<string, string>;
modulePaths?: Array<string>;
// Transforms
transform?: Record<string, string>;
transformIgnorePatterns?: Array<string>;
// Setup
setupFiles?: Array<string>;
setupFilesAfterEnv?: Array<string>;
// Coverage
collectCoverage?: boolean;
collectCoverageFrom?: Array<string>;
coverageDirectory?: string;
coveragePathIgnorePatterns?: Array<string>;
coverageProvider?: "babel" | "v8";
coverageReporters?: Array<string>;
coverageThreshold?: Record<string, Record<string, number>>;
// Execution
maxWorkers?: number | string;
testTimeout?: number;
// Output
verbose?: boolean;
silent?: boolean;
// Project structure
rootDir?: string;
roots?: Array<string>;
projects?: Array<string | Config>;
}
/**
* Global configuration that applies to the entire Jest run
*/
interface Config.GlobalConfig {
bail: number;
changedFilesWithAncestor: boolean;
changedSince?: string;
ci: boolean;
collectCoverage: boolean;
collectCoverageFrom: Array<string>;
collectCoverageOnlyFrom?: Record<string, boolean>;
coverageDirectory: string;
coveragePathIgnorePatterns?: Array<string>;
coverageProvider: "babel" | "v8";
coverageReporters: Array<string>;
coverageThreshold?: Record<string, Record<string, number>>;
detectLeaks: boolean;
detectOpenHandles: boolean;
errorOnDeprecated: boolean;
expand: boolean;
filter?: string;
findRelatedTests: boolean;
forceExit: boolean;
json: boolean;
globalSetup?: string;
globalTeardown?: string;
lastCommit: boolean;
listTests: boolean;
logHeapUsage: boolean;
maxConcurrency: number;
maxWorkers: number;
noSCM?: boolean;
noStackTrace: boolean;
notify: boolean;
notifyMode: string;
onlyChanged: boolean;
onlyFailures: boolean;
outputFile?: string;
passWithNoTests: boolean;
projects: Array<string>;
randomize?: boolean;
rootDir: string;
runTestsByPath: boolean;
seed?: number;
silent: boolean;
skipFilter: boolean;
testFailureExitCode: number;
testNamePattern?: string;
testPathPattern: string;
testPathPatterns: Array<string>;
testResultsProcessor?: string;
testSequencer: string;
testTimeout?: number;
updateSnapshot: SnapshotUpdateState;
useStderr: boolean;
verbose?: boolean;
watch: boolean;
watchAll: boolean;
watchPathIgnorePatterns: Array<string>;
watchman: boolean;
}
/**
* Per-project configuration
*/
interface Config.ProjectConfig {
automock: boolean;
cache: boolean;
cacheDirectory: string;
clearMocks: boolean;
collectCoverageFrom: Array<string>;
coveragePathIgnorePatterns: Array<string>;
cwd: string;
dependencyExtractor?: string;
detectLeaks: boolean;
displayName?: string;
errorOnDeprecated: boolean;
extensionsToTreatAsEsm: Array<string>;
extraGlobals: Array<string>;
forceCoverageMatch: Array<string>;
globalSetup?: string;
globalTeardown?: string;
globals: Record<string, unknown>;
haste: HasteConfig;
injectGlobals: boolean;
moduleDirectories: Array<string>;
moduleFileExtensions: Array<string>;
moduleNameMapper: Array<[string, string]>;
modulePaths?: Array<string>;
modulePathIgnorePatterns: Array<string>;
preset?: string;
prettierPath: string;
resetMocks: boolean;
resetModules: boolean;
resolver?: string;
restoreMocks: boolean;
rootDir: string;
roots: Array<string>;
runner: string;
setupFiles: Array<string>;
setupFilesAfterEnv: Array<string>;
skipFilter: boolean;
skipNodeResolution?: boolean;
slowTestThreshold: number;
snapshotResolver?: string;
snapshotSerializers: Array<string>;
testEnvironment: string;
testEnvironmentOptions: Record<string, unknown>;
testLocationInResults: boolean;
testMatch: Array<string>;
testPathIgnorePatterns: Array<string>;
testRegex: Array<string | RegExp>;
testRunner: string;
testURL?: string;
timers: "real" | "fake";
transform: Array<[string, string, Record<string, unknown>]>;
transformIgnorePatterns: Array<string>;
unmockedModulePathPatterns?: Array<string>;
watchPathIgnorePatterns: Array<string>;
}
/**
* Command line arguments interface
*/
interface Config.Argv {
// Test execution
all?: boolean;
bail?: boolean | number;
ci?: boolean;
findRelatedTests?: boolean;
listTests?: boolean;
onlyChanged?: boolean;
onlyFailures?: boolean;
passWithNoTests?: boolean;
runInBand?: boolean;
runTestsByPath?: boolean;
testNamePattern?: string;
testPathPatterns?: Array<string>;
testTimeout?: number;
// Watch mode
watch?: boolean;
watchAll?: boolean;
watchPathIgnorePatterns?: Array<string>;
// Coverage
collectCoverage?: boolean;
coverage?: boolean;
collectCoverageFrom?: Array<string>;
coverageDirectory?: string;
coveragePathIgnorePatterns?: Array<string>;
coverageProvider?: "babel" | "v8";
coverageReporters?: Array<string>;
coverageThreshold?: Record<string, number>;
// Output
json?: boolean;
outputFile?: string;
verbose?: boolean;
silent?: boolean;
noStackTrace?: boolean;
color?: boolean;
colors?: boolean;
// Configuration
config?: string;
rootDir?: string;
roots?: Array<string>;
projects?: Array<string>;
maxWorkers?: number | string;
cache?: boolean;
clearCache?: boolean;
debug?: boolean;
updateSnapshot?: boolean;
}
```
### Configuration File Examples
**Basic Configuration (jest.config.js):**
```javascript
module.exports = {
// Test discovery
testMatch: ["**/__tests__/**/*.js", "**/?(*.)+(spec|test).js"],
testPathIgnorePatterns: ["/node_modules/", "/build/"],
// Test environment
testEnvironment: "jsdom",
// Module resolution
moduleDirectories: ["node_modules", "src"],
moduleFileExtensions: ["js", "json", "jsx", "ts", "tsx"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
"\\.(css|less|scss)$": "identity-obj-proxy"
},
// Transforms
transform: {
"^.+\\.jsx?$": "babel-jest",
"^.+\\.tsx?$": "ts-jest"
},
// Setup
setupFilesAfterEnv: ["<rootDir>/src/setupTests.js"],
// Coverage
collectCoverage: true,
collectCoverageFrom: [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/index.js"
],
coverageDirectory: "coverage",
coverageReporters: ["text", "lcov", "html"],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
```
**TypeScript Configuration:**
```javascript
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// TypeScript specific
globals: {
"ts-jest": {
tsconfig: "tsconfig.test.json"
}
},
// Module resolution for TypeScript
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1"
},
// File extensions
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json"],
// Transform configuration
transform: {
"^.+\\.tsx?$": "ts-jest"
},
// Test files
testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"]
};
```
**Multi-Project Configuration:**
```javascript
module.exports = {
projects: [
{
displayName: "client",
testMatch: ["<rootDir>/packages/client/**/*.test.js"],
testEnvironment: "jsdom",
setupFilesAfterEnv: ["<rootDir>/packages/client/src/setupTests.js"]
},
{
displayName: "server",
testMatch: ["<rootDir>/packages/server/**/*.test.js"],
testEnvironment: "node",
setupFilesAfterEnv: ["<rootDir>/packages/server/src/setupTests.js"]
},
{
displayName: "shared",
testMatch: ["<rootDir>/packages/shared/**/*.test.js"],
testEnvironment: "node"
}
],
// Global coverage settings
collectCoverage: true,
coverageDirectory: "coverage",
coverageReporters: ["text-summary", "html"]
};
```
### Configuration Loading and Resolution
Jest resolves configuration in the following order:
1. `--config` CLI argument
2. `jest.config.js` file
3. `jest.config.ts` file
4. `jest` field in `package.json`
5. Default configuration
**Programmatic Configuration:**
```typescript
import { runCLI, buildArgv } from "jest";
// Override configuration programmatically
async function runWithCustomConfig() {
const customConfig = {
testMatch: ["**/*.custom.test.js"],
testEnvironment: "node",
collectCoverage: true
};
const argv = await buildArgv([
`--config=${JSON.stringify(customConfig)}`
]);
return runCLI(argv, [process.cwd()]);
}
// Merge with existing configuration
async function extendConfiguration(baseConfigPath: string) {
const baseConfig = require(baseConfigPath);
const extendedConfig = {
...baseConfig,
collectCoverage: true,
coverageThreshold: {
global: {
branches: 90,
functions: 90,
lines: 90,
statements: 90
}
}
};
const argv = await buildArgv([
`--config=${JSON.stringify(extendedConfig)}`
]);
return runCLI(argv, [process.cwd()]);
}
```
### Environment-Specific Configuration
**Development Configuration:**
```javascript
// jest.dev.config.js
module.exports = {
...require('./jest.config.js'),
// Development specific settings
verbose: true,
collectCoverage: false,
watchAll: true,
// Faster transforms for development
transform: {
"^.+\\.jsx?$": "babel-jest"
}
};
```
**CI Configuration:**
```javascript
// jest.ci.config.js
module.exports = {
...require('./jest.config.js'),
// CI specific settings
ci: true,
collectCoverage: true,
coverageReporters: ["text", "lcov"],
maxWorkers: "50%",
cache: false,
// Strict coverage requirements
coverageThreshold: {
global: {
branches: 90,
functions: 90,
lines: 90,
statements: 90
}
}
};
```
### Advanced Configuration Patterns
**Dynamic Configuration:**
```javascript
// jest.config.js
const isCI = process.env.CI === "true";
const isDevelopment = process.env.NODE_ENV === "development";
module.exports = {
testMatch: ["**/__tests__/**/*.js", "**/?(*.)+(spec|test).js"],
// Dynamic coverage settings
collectCoverage: isCI,
coverageReporters: isCI
? ["text", "lcov"]
: ["text-summary"],
// Dynamic worker settings
maxWorkers: isCI ? "50%" : 1,
// Development specific settings
verbose: isDevelopment,
silent: isCI,
// Environment specific transforms
transform: isDevelopment
? { "^.+\\.jsx?$": "babel-jest" }
: { "^.+\\.jsx?$": ["babel-jest", { cacheDirectory: false }] }
};
```
**Conditional Test Patterns:**
```javascript
module.exports = {
// Base test patterns
testMatch: ["**/__tests__/**/*.js"],
// Add integration tests in CI
...(process.env.CI && {
testMatch: [
"**/__tests__/**/*.js",
"**/integration/**/*.test.js"
]
}),
// Add e2e tests for full test runs
...(process.env.FULL_TEST_SUITE && {
testMatch: [
"**/__tests__/**/*.js",
"**/integration/**/*.test.js",
"**/e2e/**/*.test.js"
]
})
};
```
Jest's configuration system provides complete control over test execution behavior, enabling optimization for different environments and use cases while maintaining consistency across development workflows.

View file

@ -0,0 +1,212 @@
# Jest
Jest is a comprehensive JavaScript testing framework designed for delightful testing experiences. It provides a complete testing solution that works out of the box for most JavaScript projects, featuring instant feedback through intelligent watch mode, powerful snapshot testing, extensive mocking capabilities, built-in code coverage reporting, and seamless integration with modern JavaScript tooling including Babel, TypeScript, webpack, and various bundlers.
## Package Information
- **Package Name**: jest
- **Package Type**: npm
- **Language**: TypeScript/JavaScript
- **Installation**: `npm install jest`
## Core Imports
```typescript
import { runCLI, createTestScheduler, SearchSource, getVersion, run, buildArgv } from "jest";
import type { Config } from "jest";
```
For CommonJS:
```javascript
const { runCLI, createTestScheduler, SearchSource, getVersion, run, buildArgv } = require("jest");
```
## Basic Usage
Jest can be used programmatically or via CLI:
```typescript
import { runCLI } from "jest";
// Run Jest programmatically
const { results, globalConfig } = await runCLI(
{ roots: ["<rootDir>/src"], testMatch: ["**/__tests__/**/*.test.js"] },
["./src"]
);
console.log(`Tests completed: ${results.numTotalTests}`);
console.log(`Tests passed: ${results.numPassedTests}`);
```
CLI usage:
```bash
# Run all tests
jest
# Run tests in watch mode
jest --watch
# Run with coverage
jest --coverage
# Run specific test files
jest user.test.js
```
## Architecture
Jest is built around several key components:
- **Test Runner**: Core engine that discovers, schedules, and executes tests
- **CLI Interface**: Command-line interface for running tests with extensive options
- **SearchSource**: Test file discovery and filtering system
- **TestScheduler**: Test execution scheduling and reporter management
- **Configuration System**: Flexible configuration for projects and global settings
- **Reporter System**: Extensible test result reporting and output formatting
## Capabilities
### Test Running and Execution
Core test running functionality including programmatic API and CLI runner with comprehensive test discovery and execution capabilities.
```typescript { .api }
function runCLI(
argv: Config.Argv,
projects: Array<string>
): Promise<{
results: AggregatedResult;
globalConfig: Config.GlobalConfig;
}>;
function run(maybeArgv?: Array<string>, project?: string): Promise<void>;
```
[Test Runner](./test-runner.md)
### CLI Usage and Options
Command-line interface providing over 70 options for test execution, coverage collection, watch mode, output formatting, and project configuration.
```typescript { .api }
function buildArgv(maybeArgv?: Array<string>): Promise<Config.Argv>;
```
[CLI Usage](./cli-usage.md)
### Test Discovery and Search
Advanced test file discovery system with pattern matching, dependency tracking, and change detection for optimized test runs.
```typescript { .api }
class SearchSource {
constructor(context: TestContext);
isTestFilePath(path: string): boolean;
findMatchingTests(testPathPatternsExecutor: TestPathPatternsExecutor): SearchResult;
findTestsByPaths(paths: Array<string>): SearchResult;
findRelatedTests(allPaths: Set<string>, collectCoverage: boolean): Promise<SearchResult>;
}
```
[Test Discovery](./test-discovery.md)
### Configuration Management
Comprehensive configuration system supporting global settings, per-project configuration, and extensive customization options for all aspects of test execution.
```typescript { .api }
interface Config {
// Main configuration interface (alias for Config.InitialOptions)
testEnvironment?: string;
testMatch?: Array<string>;
transform?: Record<string, string>;
setupFilesAfterEnv?: Array<string>;
moduleNameMapping?: Record<string, string>;
collectCoverage?: boolean;
}
interface Config.GlobalConfig {
bail: number;
collectCoverage: boolean;
maxWorkers: number;
rootDir: string;
watch: boolean;
watchAll: boolean;
}
```
[Configuration](./configuration.md)
### Test Scheduling and Reporting
Test execution scheduling with multi-process coordination, reporter management, and comprehensive result aggregation.
```typescript { .api }
function createTestScheduler(
globalConfig: Config.GlobalConfig,
context: TestSchedulerContext
): Promise<TestScheduler>;
class TestScheduler {
constructor(globalConfig: Config.GlobalConfig, context: TestSchedulerContext);
addReporter(reporter: Reporter): void;
scheduleTests(tests: Array<Test>, watcher: TestWatcher): Promise<AggregatedResult>;
}
```
[Test Scheduling](./test-scheduling.md)
## Utility Functions
```typescript { .api }
function getVersion(): string;
```
Returns the current Jest version.
## Core Types
```typescript { .api }
interface AggregatedResult {
numTotalTests: number;
numPassedTests: number;
numFailedTests: number;
numPendingTests: number;
numRuntimeErrorTestSuites: number;
numTotalTestSuites: number;
numPassedTestSuites: number;
numFailedTestSuites: number;
numPendingTestSuites: number;
openHandles: Array<Error>;
snapshot: SnapshotSummary;
success: boolean;
startTime: number;
testResults: Array<TestResult>;
wasInterrupted: boolean;
}
interface SearchResult {
noSCM?: boolean;
stats?: Stats;
collectCoverageFrom?: Set<string>;
tests: Array<Test>;
total?: number;
}
interface TestContext {
config: Config.ProjectConfig;
hasteFS: IHasteFS;
moduleMap: IModuleMap;
resolver: IResolver;
}
interface TestSchedulerContext extends ReporterContext, TestRunnerContext {}
interface Test {
context: TestContext;
duration?: number;
path: string;
}
```

View file

@ -0,0 +1,307 @@
# Test Discovery
Jest's test discovery system provides advanced capabilities for finding, filtering, and organizing test files with pattern matching, dependency tracking, and change detection for optimized test runs.
## Capabilities
### SearchSource Class
The SearchSource class is the core component responsible for test file discovery and filtering.
```typescript { .api }
/**
* Core class for finding and filtering test files
*/
class SearchSource {
constructor(context: TestContext);
/**
* Determines if a given path is a test file based on configuration
* @param path - File path to check
* @returns True if the path matches test file patterns
*/
isTestFilePath(path: string): boolean;
/**
* Finds tests matching the given path patterns
* @param testPathPatternsExecutor - Pattern executor for test paths
* @returns Search results with matching tests
*/
findMatchingTests(testPathPatternsExecutor: TestPathPatternsExecutor): SearchResult;
/**
* Finds tests by exact file paths
* @param paths - Array of exact file paths
* @returns Search results with specified test files
*/
findTestsByPaths(paths: Array<string>): SearchResult;
/**
* Finds tests related to the given source file paths
* @param allPaths - Set of source file paths
* @param collectCoverage - Whether to collect coverage information
* @returns Promise resolving to search results with related tests
*/
findRelatedTests(allPaths: Set<string>, collectCoverage: boolean): Promise<SearchResult>;
/**
* Main method to get test paths based on configuration and options
* @param globalConfig - Global Jest configuration
* @param projectConfig - Project-specific configuration
* @param changedFiles - Optional changed files information
* @param filter - Optional filter function
* @returns Promise resolving to comprehensive search results
*/
getTestPaths(
globalConfig: Config.GlobalConfig,
projectConfig: Config.ProjectConfig,
changedFiles?: ChangedFiles,
filter?: Filter
): Promise<SearchResult>;
}
interface SearchResult {
noSCM?: boolean;
stats?: Stats;
collectCoverageFrom?: Set<string>;
tests: Array<Test>;
total?: number;
}
interface Stats {
roots: number;
testMatch: number;
testPathIgnorePatterns: number;
testRegex: number;
testPathPatterns?: number;
}
```
**Usage Examples:**
```typescript
import { SearchSource } from "jest";
// Create SearchSource instance
const searchSource = new SearchSource(testContext);
// Find all test files
const allTests = await searchSource.getTestPaths(
globalConfig,
projectConfig
);
console.log(`Found ${allTests.tests.length} test files`);
// Check if a file is a test file
const isTest = searchSource.isTestFilePath("src/__tests__/utils.test.js");
console.log(`Is test file: ${isTest}`);
// Find tests related to changed source files
const changedFiles = new Set(["src/utils.js", "src/components/Button.js"]);
const relatedTests = await searchSource.findRelatedTests(changedFiles, false);
console.log(`Found ${relatedTests.tests.length} related tests`);
```
### Test Pattern Matching
Advanced pattern matching for test file discovery:
```typescript
// Find tests by exact paths
const specificTests = searchSource.findTestsByPaths([
"src/components/Button.test.js",
"src/utils/helpers.test.js"
]);
// Find tests matching patterns (via getTestPaths)
const patternTests = await searchSource.getTestPaths(
{
...globalConfig,
testPathPatterns: ["components", "utils"]
},
projectConfig
);
```
### Change Detection Integration
Optimize test runs by finding tests related to changed files:
```typescript
import { SearchSource } from "jest";
async function runTestsForChangedFiles(
searchSource: SearchSource,
changedFiles: string[]
) {
// Find tests related to changed source files
const relatedTests = await searchSource.findRelatedTests(
new Set(changedFiles),
true // collectCoverage
);
if (relatedTests.tests.length === 0) {
console.log("No tests found for changed files");
return null;
}
return relatedTests;
}
// Usage with git integration
async function findTestsForGitChanges() {
const changedFiles = await getChangedFilesFromGit();
const relatedTests = await searchSource.findRelatedTests(
new Set(changedFiles),
false
);
return relatedTests.tests.map(test => test.path);
}
```
### Custom Test Discovery Patterns
Implement custom test discovery logic:
```typescript
import { SearchSource } from "jest";
class CustomTestDiscovery {
constructor(private searchSource: SearchSource) {}
async findTestsByFeature(featureName: string) {
// Find all tests
const allTests = await this.searchSource.getTestPaths(
globalConfig,
projectConfig
);
// Filter by feature directory or naming convention
const featureTests = allTests.tests.filter(test =>
test.path.includes(`features/${featureName}`) ||
test.path.includes(`${featureName}.test.`)
);
return {
...allTests,
tests: featureTests,
total: featureTests.length
};
}
async findTestsByTags(tags: string[]) {
const allTests = await this.searchSource.getTestPaths(
globalConfig,
projectConfig
);
// Filter tests based on file content or naming patterns
const taggedTests = allTests.tests.filter(test => {
const filename = test.path.toLowerCase();
return tags.some(tag => filename.includes(tag.toLowerCase()));
});
return {
...allTests,
tests: taggedTests,
total: taggedTests.length
};
}
async findSlowTests(thresholdMs: number = 1000) {
// This would typically require historical test timing data
// Implementation would depend on custom test result storage
const allTests = await this.searchSource.getTestPaths(
globalConfig,
projectConfig
);
// Example: identify tests by naming convention
const potentiallySlowTests = allTests.tests.filter(test =>
test.path.includes("integration") ||
test.path.includes("e2e") ||
test.path.includes("slow")
);
return {
...allTests,
tests: potentiallySlowTests,
total: potentiallySlowTests.length
};
}
}
```
### Performance Optimization
Optimize test discovery for large codebases:
```typescript
async function optimizedTestDiscovery(
searchSource: SearchSource,
options: {
useCache?: boolean;
maxFiles?: number;
changedFilesOnly?: boolean;
} = {}
) {
if (options.changedFilesOnly) {
// Only find tests related to changed files
const changedFiles = await getChangedFiles();
return searchSource.findRelatedTests(new Set(changedFiles), false);
}
// Get all tests with potential limits
const allTests = await searchSource.getTestPaths(
globalConfig,
projectConfig
);
if (options.maxFiles && allTests.tests.length > options.maxFiles) {
// Limit test count for performance
const limitedTests = allTests.tests.slice(0, options.maxFiles);
console.warn(`Limited to ${options.maxFiles} tests (found ${allTests.tests.length})`);
return {
...allTests,
tests: limitedTests,
total: limitedTests.length
};
}
return allTests;
}
```
## Integration with Test Execution
The SearchSource integrates seamlessly with Jest's test execution pipeline:
```typescript
import { SearchSource, createTestScheduler } from "jest";
async function discoverAndRunTests() {
// 1. Discover tests
const searchSource = new SearchSource(testContext);
const searchResult = await searchSource.getTestPaths(
globalConfig,
projectConfig
);
// 2. Create scheduler
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
// 3. Execute discovered tests
const results = await scheduler.scheduleTests(
searchResult.tests,
testWatcher
);
return results;
}
```
Jest's test discovery system provides the foundation for intelligent test execution, enabling optimized test runs based on code changes, file patterns, and custom discovery logic.

View file

@ -0,0 +1,280 @@
# Test Runner
Jest's test runner provides both programmatic and CLI-based test execution with comprehensive configuration options, result aggregation, and performance optimization through parallel execution.
## Capabilities
### Programmatic Test Execution
Run Jest tests programmatically with full control over configuration and execution parameters.
```typescript { .api }
/**
* Runs Jest CLI programmatically with specified configuration and projects
* @param argv - Command line arguments and configuration
* @param projects - Array of project paths to run tests on
* @returns Promise resolving to test results and global configuration
*/
function runCLI(
argv: Config.Argv,
projects: Array<string>
): Promise<{
results: AggregatedResult;
globalConfig: Config.GlobalConfig;
}>;
interface AggregatedResult {
numTotalTests: number;
numPassedTests: number;
numFailedTests: number;
numPendingTests: number;
numRuntimeErrorTestSuites: number;
numTotalTestSuites: number;
numPassedTestSuites: number;
numFailedTestSuites: number;
numPendingTestSuites: number;
openHandles: Array<Error>;
snapshot: SnapshotSummary;
success: boolean;
startTime: number;
testResults: Array<TestResult>;
wasInterrupted: boolean;
}
```
**Usage Examples:**
```typescript
import { runCLI, buildArgv } from "jest";
// Basic test run
async function runTests() {
const argv = await buildArgv(["--testMatch=**/*.test.js"]);
const { results, globalConfig } = await runCLI(argv, ["./src"]);
console.log(`${results.numPassedTests}/${results.numTotalTests} tests passed`);
if (!results.success) {
console.error("Tests failed!");
process.exit(1);
}
}
// Run with coverage and JSON output
async function runTestsWithCoverage() {
const argv = await buildArgv([
"--coverage",
"--json",
"--outputFile=test-results.json"
]);
const { results } = await runCLI(argv, [process.cwd()]);
return results;
}
// Run specific test files
async function runSpecificTests(testFiles: string[]) {
const argv = await buildArgv([
"--runTestsByPath",
...testFiles
]);
const { results } = await runCLI(argv, [process.cwd()]);
return results;
}
```
### CLI Entry Point
Main CLI runner that handles argument parsing and delegates to the programmatic API.
```typescript { .api }
/**
* Main CLI entry point for Jest
* @param maybeArgv - Optional command line arguments (defaults to process.argv.slice(2))
* @param project - Optional project path
* @returns Promise that resolves when Jest execution completes
*/
function run(maybeArgv?: Array<string>, project?: string): Promise<void>;
```
**Usage Example:**
```typescript
import { run } from "jest";
// Run Jest with default arguments
await run();
// Run Jest with custom arguments
await run(["--watch", "--testPathPatterns=src/components"]);
// Run Jest for specific project
await run(["--coverage"], "./my-project");
```
### Argument Parsing and Validation
Parse and validate command line arguments for Jest execution.
```typescript { .api }
/**
* Builds and validates command line arguments for Jest
* @param maybeArgv - Optional command line arguments
* @returns Promise resolving to parsed and validated arguments
*/
function buildArgv(maybeArgv?: Array<string>): Promise<Config.Argv>;
interface Config.Argv {
// Test execution options
all?: boolean;
bail?: boolean | number;
findRelatedTests?: boolean;
listTests?: boolean;
onlyChanged?: boolean;
onlyFailures?: boolean;
passWithNoTests?: boolean;
runInBand?: boolean;
runTestsByPath?: boolean;
testNamePattern?: string;
testPathPatterns?: Array<string>;
testTimeout?: number;
// Watch mode options
watch?: boolean;
watchAll?: boolean;
watchPathIgnorePatterns?: Array<string>;
// Coverage options
collectCoverage?: boolean;
coverage?: boolean;
collectCoverageFrom?: Array<string>;
coverageDirectory?: string;
coveragePathIgnorePatterns?: Array<string>;
coverageProvider?: "babel" | "v8";
coverageReporters?: Array<string>;
coverageThreshold?: Record<string, number>;
// Output options
json?: boolean;
outputFile?: string;
verbose?: boolean;
silent?: boolean;
noStackTrace?: boolean;
color?: boolean;
colors?: boolean;
// Configuration options
config?: string;
rootDir?: string;
roots?: Array<string>;
projects?: Array<string>;
maxWorkers?: number | string;
cache?: boolean;
clearCache?: boolean;
debug?: boolean;
updateSnapshot?: boolean;
}
```
### Integration Patterns
Common patterns for integrating Jest into build tools and custom workflows:
**Build Tool Integration:**
```typescript
import { runCLI, buildArgv } from "jest";
async function buildToolIntegration(options: {
testFiles?: string[];
coverage?: boolean;
watch?: boolean;
}) {
const args = [];
if (options.coverage) args.push("--coverage");
if (options.watch) args.push("--watch");
if (options.testFiles) {
args.push("--runTestsByPath", ...options.testFiles);
}
const argv = await buildArgv(args);
const { results } = await runCLI(argv, [process.cwd()]);
return {
success: results.success,
testCount: results.numTotalTests,
passedTests: results.numPassedTests,
failedTests: results.numFailedTests,
coverageMap: results.coverageMap
};
}
```
**CI/CD Integration:**
```typescript
import { runCLI, buildArgv } from "jest";
async function runTestsInCI() {
const argv = await buildArgv([
"--ci",
"--coverage",
"--json",
"--outputFile=test-results.json",
"--coverageReporters=text-lcov",
"--coverageDirectory=coverage"
]);
try {
const { results, globalConfig } = await runCLI(argv, [process.cwd()]);
// Log summary
console.log(`Tests: ${results.numPassedTests}/${results.numTotalTests} passed`);
console.log(`Test Suites: ${results.numPassedTestSuites}/${results.numTotalTestSuites} passed`);
if (!results.success) {
console.error("❌ Tests failed");
process.exit(1);
}
console.log("✅ All tests passed");
} catch (error) {
console.error("Error running tests:", error);
process.exit(1);
}
}
```
**Custom Test Discovery:**
```typescript
import { runCLI, buildArgv } from "jest";
import * as fs from "fs";
import * as path from "path";
async function runTestsForChangedFiles(changedFiles: string[]) {
// Find test files related to changed source files
const testFiles = changedFiles
.filter(file => file.endsWith('.js') || file.endsWith('.ts'))
.map(file => {
const testFile = file.replace(/\.(js|ts)$/, '.test.$1');
return fs.existsSync(testFile) ? testFile : null;
})
.filter(Boolean) as string[];
if (testFiles.length === 0) {
console.log("No test files found for changed files");
return;
}
const argv = await buildArgv([
"--runTestsByPath",
...testFiles
]);
const { results } = await runCLI(argv, [process.cwd()]);
return results;
}
```

View file

@ -0,0 +1,501 @@
# Test Scheduling
Jest's test scheduling system manages test execution coordination, multi-process scheduling, reporter management, and comprehensive result aggregation for optimal performance and reporting.
## Capabilities
### TestScheduler Class
The TestScheduler manages test execution scheduling and coordinates with reporters for comprehensive test result processing.
```typescript { .api }
/**
* Manages test execution scheduling and reporting
*/
class TestScheduler {
constructor(globalConfig: Config.GlobalConfig, context: TestSchedulerContext);
/**
* Adds a reporter to the dispatcher
* @param reporter - Reporter instance to add
*/
addReporter(reporter: Reporter): void;
/**
* Removes a reporter from the dispatcher
* @param reporterConstructor - Constructor of reporter to remove
*/
removeReporter(reporterConstructor: ReporterConstructor): void;
/**
* Schedules and executes the given tests
* @param tests - Array of test files to execute
* @param watcher - Test watcher for watch mode integration
* @returns Promise resolving to aggregated test results
*/
scheduleTests(tests: Array<Test>, watcher: TestWatcher): Promise<AggregatedResult>;
}
/**
* Factory function that creates a TestScheduler and sets up reporters
* @param globalConfig - Global Jest configuration
* @param context - Scheduler context including reporter and test runner contexts
* @returns Promise resolving to configured test scheduler
*/
function createTestScheduler(
globalConfig: Config.GlobalConfig,
context: TestSchedulerContext
): Promise<TestScheduler>;
type TestSchedulerContext = ReporterContext & TestRunnerContext;
type ReporterConstructor = new (
globalConfig: Config.GlobalConfig,
reporterConfig: Record<string, unknown>,
reporterContext: ReporterContext,
) => JestReporter;
```
**Usage Examples:**
```typescript
import { createTestScheduler, SearchSource } from "jest";
// Create and configure test scheduler
async function setupTestScheduler() {
const scheduler = await createTestScheduler(globalConfig, {
...reporterContext,
...testRunnerContext
});
// Add custom reporter
scheduler.addReporter(new CustomReporter(globalConfig, {}, reporterContext));
return scheduler;
}
// Execute tests with scheduler
async function executeTests(testFiles: Array<Test>) {
const scheduler = await setupTestScheduler();
const results = await scheduler.scheduleTests(
testFiles,
new TestWatcher({ isWatchMode: false })
);
return results;
}
```
### Test Execution Coordination
The TestScheduler coordinates test execution across multiple processes and manages the complete test lifecycle.
**Basic Test Scheduling:**
```typescript
import { createTestScheduler, SearchSource } from "jest";
async function coordinateTestExecution() {
// 1. Discover tests
const searchSource = new SearchSource(testContext);
const searchResult = await searchSource.getTestPaths(
globalConfig,
projectConfig
);
// 2. Create scheduler
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
// 3. Schedule and execute tests
const results = await scheduler.scheduleTests(
searchResult.tests,
new TestWatcher({ isWatchMode: false })
);
console.log(`Executed ${results.numTotalTests} tests`);
console.log(`Passed: ${results.numPassedTests}`);
console.log(`Failed: ${results.numFailedTests}`);
return results;
}
```
### Reporter Management
Manage test result reporting through dynamic reporter configuration.
```typescript
import { createTestScheduler } from "jest";
// Custom reporter for specialized output
class CustomReporter {
constructor(
private globalConfig: Config.GlobalConfig,
private options: Record<string, unknown>,
private context: ReporterContext
) {}
onRunStart(results: AggregatedResult, options: ReporterOnStartOptions) {
console.log("Starting test run...");
}
onTestResult(test: Test, testResult: TestResult, results: AggregatedResult) {
if (testResult.testResults.some(result => result.status === "failed")) {
console.log(`❌ ${test.path}`);
} else {
console.log(`✅ ${test.path}`);
}
}
onRunComplete(contexts: Set<TestContext>, results: AggregatedResult) {
console.log(`Test run completed: ${results.success ? "PASSED" : "FAILED"}`);
}
}
// Configure scheduler with custom reporters
async function setupCustomReporting() {
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
// Add multiple reporters
scheduler.addReporter(new CustomReporter(globalConfig, {}, reporterContext));
scheduler.addReporter(new JSONReporter(globalConfig, { outputFile: "results.json" }, reporterContext));
// Remove default reporter if needed
scheduler.removeReporter(DefaultReporter);
return scheduler;
}
```
### Multi-Process Coordination
Handle test execution across multiple worker processes for optimal performance.
```typescript
import { createTestScheduler } from "jest";
async function scheduleTestsWithWorkers(maxWorkers: number) {
const globalConfig = {
...baseGlobalConfig,
maxWorkers,
runInBand: maxWorkers === 1
};
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
// Configure for multi-process execution
const results = await scheduler.scheduleTests(
testFiles,
new TestWatcher({
isWatchMode: false,
// Additional options for worker coordination
})
);
return results;
}
// Adaptive worker configuration
async function adaptiveTestScheduling(testCount: number) {
// Determine optimal worker count based on test count and system resources
const maxWorkers = Math.min(
Math.max(1, Math.floor(testCount / 10)), // At least 10 tests per worker
require("os").cpus().length, // Don't exceed CPU count
8 // Cap at 8 workers
);
return scheduleTestsWithWorkers(maxWorkers);
}
```
### Watch Mode Integration
Integrate with Jest's watch mode for automatic test re-execution.
```typescript
import { createTestScheduler } from "jest";
import { TestWatcher } from "jest-watcher";
async function scheduleTestsInWatchMode() {
const scheduler = await createTestScheduler(
{ ...globalConfig, watch: true },
schedulerContext
);
const watcher = new TestWatcher({ isWatchMode: true });
// Watch mode provides automatic re-scheduling
const results = await scheduler.scheduleTests(testFiles, watcher);
// In watch mode, this promise typically never resolves
// as Jest continues watching for file changes
return results;
}
// Custom watch mode logic
class CustomTestWatcher extends TestWatcher {
constructor(options: { isWatchMode: boolean }) {
super(options);
}
async onChange(changedFiles: Set<string>) {
console.log(`Files changed: ${Array.from(changedFiles).join(", ")}`);
// Custom logic for determining which tests to re-run
const searchSource = new SearchSource(testContext);
const relatedTests = await searchSource.findRelatedTests(changedFiles, false);
// Re-schedule only related tests
if (relatedTests.tests.length > 0) {
await this.scheduler.scheduleTests(relatedTests.tests, this);
}
}
}
```
### Result Aggregation and Processing
Process and aggregate test results for comprehensive reporting.
```typescript
import { createTestScheduler } from "jest";
interface TestExecutionMetrics {
totalDuration: number;
averageTestDuration: number;
slowestTests: Array<{ path: string; duration: number }>;
fastestTests: Array<{ path: string; duration: number }>;
failureRate: number;
coveragePercentage?: number;
}
async function executeWithMetrics(tests: Array<Test>): Promise<{
results: AggregatedResult;
metrics: TestExecutionMetrics;
}> {
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
const startTime = Date.now();
const results = await scheduler.scheduleTests(
tests,
new TestWatcher({ isWatchMode: false })
);
const totalDuration = Date.now() - startTime;
// Calculate metrics
const testDurations = results.testResults
.flatMap(suite => suite.testResults)
.map(test => ({ path: test.title, duration: test.duration || 0 }))
.filter(test => test.duration > 0);
const averageTestDuration = testDurations.length > 0
? testDurations.reduce((sum, test) => sum + test.duration, 0) / testDurations.length
: 0;
const sortedByDuration = testDurations.sort((a, b) => b.duration - a.duration);
const metrics: TestExecutionMetrics = {
totalDuration,
averageTestDuration,
slowestTests: sortedByDuration.slice(0, 5),
fastestTests: sortedByDuration.slice(-5).reverse(),
failureRate: results.numTotalTests > 0
? results.numFailedTests / results.numTotalTests
: 0,
coveragePercentage: results.coverageMap
? calculateCoveragePercentage(results.coverageMap)
: undefined
};
return { results, metrics };
}
function calculateCoveragePercentage(coverageMap: any): number {
// Implementation would depend on coverage map structure
// This is a simplified example
const summary = coverageMap.getCoverageSummary?.();
return summary?.lines?.pct || 0;
}
```
### Error Handling and Recovery
Implement robust error handling for test scheduling failures.
```typescript
import { createTestScheduler } from "jest";
async function robustTestScheduling(tests: Array<Test>) {
let scheduler: TestScheduler;
try {
scheduler = await createTestScheduler(globalConfig, schedulerContext);
} catch (error) {
console.error("Failed to create test scheduler:", error);
throw new Error("Test scheduler initialization failed");
}
// Add error reporter
scheduler.addReporter(new ErrorTrackingReporter());
try {
const results = await scheduler.scheduleTests(
tests,
new TestWatcher({ isWatchMode: false })
);
// Check for critical failures
if (results.numRuntimeErrorTestSuites > 0) {
console.warn(`${results.numRuntimeErrorTestSuites} test suites had runtime errors`);
}
// Handle open handles
if (results.openHandles && results.openHandles.length > 0) {
console.warn(`${results.openHandles.length} open handles detected`);
// Optionally force exit
if (globalConfig.forceExit) {
process.exit(results.success ? 0 : 1);
}
}
return results;
} catch (error) {
console.error("Test execution failed:", error);
// Attempt recovery or cleanup
if (error.message.includes("worker")) {
console.log("Retrying with single worker...");
const fallbackConfig = { ...globalConfig, maxWorkers: 1, runInBand: true };
const fallbackScheduler = await createTestScheduler(fallbackConfig, schedulerContext);
return fallbackScheduler.scheduleTests(tests, new TestWatcher({ isWatchMode: false }));
}
throw error;
}
}
class ErrorTrackingReporter {
private errors: Array<{ test: string; error: any }> = [];
onTestResult(test: Test, testResult: TestResult) {
testResult.testResults.forEach(result => {
if (result.status === "failed") {
this.errors.push({
test: `${test.path} > ${result.title}`,
error: result.failureMessages
});
}
});
}
onRunComplete() {
if (this.errors.length > 0) {
console.log("\n=== Test Failures Summary ===");
this.errors.forEach(({ test, error }) => {
console.log(`\n❌ ${test}`);
console.log(error.join("\n"));
});
}
}
}
```
### Performance Optimization
Optimize test scheduling for different scenarios and constraints.
```typescript
import { createTestScheduler } from "jest";
interface SchedulingStrategy {
name: string;
configure: (config: Config.GlobalConfig) => Config.GlobalConfig;
}
const schedulingStrategies: Record<string, SchedulingStrategy> = {
fast: {
name: "Fast Execution",
configure: (config) => ({
...config,
maxWorkers: "100%",
cache: true,
bail: 1 // Stop on first failure
})
},
thorough: {
name: "Thorough Testing",
configure: (config) => ({
...config,
maxWorkers: "50%",
collectCoverage: true,
bail: false
})
},
debug: {
name: "Debug Mode",
configure: (config) => ({
...config,
maxWorkers: 1,
runInBand: true,
verbose: true,
detectOpenHandles: true
})
},
ci: {
name: "CI Optimized",
configure: (config) => ({
...config,
ci: true,
maxWorkers: "50%",
cache: false,
collectCoverage: true,
coverageReporters: ["text", "lcov"]
})
}
};
async function scheduleWithStrategy(
tests: Array<Test>,
strategyName: keyof typeof schedulingStrategies
) {
const strategy = schedulingStrategies[strategyName];
const optimizedConfig = strategy.configure(globalConfig);
console.log(`Using ${strategy.name} strategy`);
const scheduler = await createTestScheduler(optimizedConfig, schedulerContext);
return scheduler.scheduleTests(
tests,
new TestWatcher({ isWatchMode: false })
);
}
// Auto-select strategy based on environment
async function smartScheduling(tests: Array<Test>) {
const isCI = process.env.CI === "true";
const isDebug = process.env.DEBUG === "true";
const testCount = tests.length;
let strategy: keyof typeof schedulingStrategies;
if (isDebug) {
strategy = "debug";
} else if (isCI) {
strategy = "ci";
} else if (testCount < 10) {
strategy = "fast";
} else {
strategy = "thorough";
}
return scheduleWithStrategy(tests, strategy);
}
```
Jest's test scheduling system provides complete control over test execution coordination, enabling optimized performance, comprehensive reporting, and reliable test execution across different environments and use cases.

Some files were not shown because too many files have changed in this diff Show more