find_alias_files descended into every entry whose is_dir flag was set.
On Windows that flag is also set for a directory junction and a directory
symbolic link, so the tsconfig/jsconfig walk could leave the repository
through a link. Discovery and the incremental walker already skip links
(safe_stat, cbm_path_info_utf8().is_symlink); the alias walk now does the
same on every platform, and skips a child whose metadata cannot be read.
On POSIX the walk already stopped at links because readdir reports them
as DT_LNK rather than DT_DIR; the symbolic-link test pins that contract.
The junction test (cmd.exe mklink /J, no privilege needed) is the case
that changes and is compiled on Windows only.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The auxiliary filesystem walks (pkgmap manifest scan, tsconfig/jsconfig
path-alias discovery, env-URL scan) ignored the directory subtrees that
discovery had already excluded (gitignore + skip dirs). On a huge
monorepo with a gitignored vendored tree this kept the pkgmap walk busy
for ~15 minutes re-traversing directories the index never uses (#792).
Distills PR #793 by Patrick (@Sowiedu): a shared root-anchored,
'/'-boundary exclusion predicate (cbm_pipeline_relpath_is_excluded) in
pipeline_internal.h, exclusion parameters threaded through
cbm_pkgmap_scan_repo / cbm_pkgmap_build_from_repo, find_alias_files /
cbm_load_path_aliases_excluded and cbm_scan_project_env_urls_excluded,
NULL-exclusion wrappers preserving the old signatures, and the borrowed
excluded_dirs/excluded_count pair on cbm_pipeline_ctx_t.
Beyond #793:
- Thread the excluded list into pipeline_incremental.c's extract ctx
and its path-alias load as well, so incremental runs stop walking
excluded trees too (#804).
- Add regression tests: boundary semantics of the exclusion predicate,
plus walk-level exclusion tests for pkgmap, path-alias, and envscan
(each with an unexcluded control run so they cannot pass vacuously).
All are red against the unfixed sources.
- Correct the envscan narrative: cbm_scan_project_env_urls has no
production callers today; the exclusion plumbing is kept for
consistency and exercised by tests.
Closes#792. Closes#804.
Co-authored-by: Patrick <11910229+Sowiedu@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
tsconfig.json `paths` entries that climb out of their config's directory
(e.g. a tsconfig at apps/web/tsconfig.json mapping "@shared/*" to
"../../packages/shared/src/*", the standard monorepo layout) resolved to
a broken path: resolve_target_relative() only stripped a single leading
"./" and otherwise concatenated dir_prefix and target verbatim, leaving
literal ".." segments in the result. Those never match a real module's
FQN, since cbm_pipeline_fqn_module tokenizes on '/' without collapsing
them - so every cross-package import through such an alias looked like
dead code.
resolve_target_relative now walks the target's path segments and pops
a directory off dir_prefix for each "..", the same normalization
cbm_pipeline_resolve_relative_import already does for plain relative
imports.
Closes#730.
Signed-off-by: Alexandros Pappas <11921291+apappas1129@users.noreply.github.com>
cbm_mkdtemp rewrites the template in place to the translated %TEMP% path on Windows, which overflowed the 23-byte 'char tmpl[]' literal (ASan stack-buffer-overflow). Use a 256-byte buffer as the cbm_mkdtemp contract requires (matching the other test callers).
Four tests hardcoded POSIX /tmp/ paths that don't exist on Windows: test_path_alias used raw mkdtemp("/tmp/...") and the store pragma/checkpoint tests built /tmp/*.db directly. Switch to cbm_mkdtemp (translates /tmp/ -> %TEMP% on Windows) and cbm_tmpdir() (matching test_store_bulk). build/smoke/soak-windows already pass; this makes the Windows unit-test job green too.
The py/cs LSP benchmark tests have time-budget asserts (<150ms / <200ms) that ASan+UBSan blow on CI (750-900ms on arm64), so they bailed before cbm_free_result and LeakSanitizer flagged the leaked arena/parser. Gate them behind CBM_SKIP_PERF (set by the dry-run) like the incremental perf tests, so they run only when perf is enabled.
Fix Windows (msys2-clang) compile errors in test code by using the existing compat shims: setenv/unsetenv -> cbm_setenv/cbm_unsetenv (test_store_pragmas, test_ts_lsp), strndup -> cbm_strndup and 2-arg mkdir -> cbm_mkdir (test_path_alias), and guard the utimensat mtime-bump in test_pipeline behind #ifndef _WIN32.
CALLS edges for cross-file function calls were mis-wired or missing in
TypeScript projects that use path aliases (e.g. @/lib/auth → src/lib/auth):
1. resolve_relative_import only handled "./relative" paths, so
"@/lib/auth"-style imports never reached IMPORTS edges. The import
map stayed empty, Strategy 1 in cbm_registry_resolve failed, and
resolution fell through to name-based lookups that pick the wrong
target when functions share names across files.
2. resolve_import_map built the candidate QN as the bare module QN
when the callee had no dot ("requireAdmin" vs "pkg.Func"), instead
of module_qn.requireAdmin. Even with a populated import map, bare
calls still mis-resolved.
Fix:
- New src/pipeline/path_alias.{h,c}: language-agnostic build-tool
path-alias subsystem. Currently parses tsconfig.json and
jsconfig.json compilerOptions.paths + baseUrl via the existing
yyjson dependency (with ALLOW_COMMENTS|ALLOW_TRAILING_COMMAS for
JSONC). The data model (cbm_path_alias_t / map / scope /
collection) is generic; adding Vite/Webpack/Python loaders means
writing a new file-parsing helper and registering it in
find_alias_files — the resolver, collection, and pipeline
integration do not change.
- O(n log n) ordering: scope-by-dir-specificity and entry-by-
prefix-specificity are sorted with qsort, replacing the O(n²)
bubble sorts that the original draft used. With caps of 256
entries per config and 256 configs per repo, the sort is
effectively flat.
- Resource ceilings with logging: directory recursion capped at
32 levels; total config files capped at 256; per-config size
capped at 64 KB. Cap hits emit cbm_log_warn so silent truncation
on pathological monorepos shows up in the index log.
- Walk hygiene: skips node_modules, .git, dist, build, .next,
coverage, target.
- Single integration point: cbm_pipeline_resolve_module gets a
Strategy 1b between relative-import resolution and pkgmap so both
the sequential and parallel pipelines pick up alias resolution
automatically. Non-TS projects pay nothing — the loader returns
NULL when no usable configs are found.
- Layer 2 fix in registry.c: resolve_import_map now builds the
candidate as resolved.prefix when suffix is NULL (one-line fix,
matching comment).
- Tests: 10 path_alias tests (basic wildcard, specificity, exact,
extension stripping, baseUrl, NULL safety, nearest-ancestor scope
selection, end-to-end loader against a tmp monorepo, no-configs
case) plus a registry test for the bare-call fix. Full suite:
2835 passed, 0 failed.
Cherry-picked and generalized from #243 (originally TypeScript-only
under tsconfig-named types). Renamed types (cbm_path_alias_scope_t /
collection_t in place of cbm_tsconfig_*) and centralised the load
into cbm_load_path_aliases so future loaders for other build tools
slot in without touching extraction or pipeline code.
Closes#243.
Co-authored-by: Peter Cox <info@petercox.ie>