Skip to content

feat: v2.8.0 - cross-platform path handling, case correction, and code quality#195

Merged
imjuni merged 15 commits intomasterfrom
develop
May 1, 2026
Merged

feat: v2.8.0 - cross-platform path handling, case correction, and code quality#195
imjuni merged 15 commits intomasterfrom
develop

Conversation

@imjuni
Copy link
Copy Markdown
Owner

@imjuni imjuni commented May 1, 2026

Summary

  • Fix cross-platform path and cwd issues (Windows/macOS compatibility)
  • Add source file case correction with buildCorrectCasePathMap
  • Improve include/exclude pipeline with verbose debug logging
  • Refactor naming and translate Korean comments to bilingual format

Changes

Features

  • buildCorrectCasePathMap: batch-correct source file casing to handle case-insensitive filesystems (macOS/Windows)
  • getCwd: honour USE_INIT_CWD / INIT_CWD environment variables
  • Debugger: add verbose list logging for include/exclude pipeline visibility

Bug Fixes

  • Restore 2.7.2 compatibility for Windows and empty output path
  • Convert absolute output path to relative before passing to ExcludeContainer
  • Propagate resolved project path to transform functions
  • Use getCwd() as base in posixResolve and createBuildOptions
  • Default to cwd-scoped glob when include is empty
  • Treat empty include patterns as no filter
  • Normalize path separators for Windows compatibility

Refactoring & Docs

  • Replace single-letter lambda params (p, sf, f) with descriptive names per coding guideline
  • Add English translations alongside Korean comments across 13 source files
  • Add variable naming guideline to PROJECT.md

Test plan

  • Run full test suite: pnpm test
  • Verify bundling/creating/moduling modes on macOS and Windows
  • Check case-insensitive path correction with type14 scenario
  • Verify include/exclude pipeline with empty include patterns
  • Confirm USE_INIT_CWD / INIT_CWD env vars are respected

🤖 Generated with Claude Code

imjuni and others added 14 commits April 11, 2026 09:57
- fix IncludeContainer.isInclude and ExcludeContainer.isExclude to
  normalize backslashes before map lookup so Windows paths
  (C:\project\src\foo.ts) correctly match posix keys stored in the map
  (C:/project/src/foo.ts)
- fix getCorrectCasedPath to always return posix-separated paths via
  replaceSepToPosix, preventing backslash-contaminated paths from
  propagating to all downstream callers on Windows
- add Debugger singleton with --verbose/-v CLI flag to emit diagnostic
  logs to stderr for path resolution tracing on Windows
- add regression tests for Windows-style backslash paths in both
  IncludeContainer and ExcludeContainer (absolute, relative, inline)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- add logList() method to Debugger for printing labeled file lists
- log ts-morph source files, include/exclude map contents, filtered
  results at each stage in creating, bundling, and moduling commands
- replace IncludeContainer constructor sample log (first 5) with full
  file list via logList
- add ExcludeContainer constructor log showing patterns, resolved map
  files, and inline excludeds

usage: ctix build --verbose 2> ctix-debug.log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When no include patterns are specified (neither via --include flag,
.ctirc config, nor tsconfig.json include field), IncludeContainer
was building an empty map and returning false for every file, causing
'Cannot find target files' on projects whose tsconfig.json omits the
include field (e.g. examples/type04, examples/type05).

- IncludeContainer.isInclude(): return true when map is empty so that
  an absent include configuration includes all source files, matching
  TypeScript compiler's default behaviour
- Add a verbose debug log when no patterns are specified to aid diagnosis
- Update the corresponding unit test to assert the corrected behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous fix returned true for all files when no include patterns
were specified, which caused ctix to process every TypeScript file
that ts-morph had loaded — including files from unrelated workspace
packages outside the target directory.

Instead, fall back to ['**/*.ts', '**/*.tsx'] resolved against the
project cwd when include is empty. This keeps the scope limited to
the project directory while preserving the TypeScript compiler's
default behaviour of including all source files when no include field
is declared.

- IncludeContainer: derive default patterns from cwd rather than
  bypassing the map lookup entirely
- Update unit test to assert the map is populated and a real project
  file passes the include check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When ctix runs as an npm/pnpm script from a parent directory the
script runner changes process.cwd() to the package root. pnpm sets
INIT_CWD to the directory where the user originally invoked the
command, and USE_INIT_CWD=true signals that ctix should use that
value instead.

- getCwd(): return process.env.INIT_CWD when USE_INIT_CWD=true so
  that all relative path resolution (config file lookup, project path,
  output path) is anchored to the user's intended directory
- Replace every direct process.cwd() call in production source files
  with getCwd() so the override is applied consistently:
  getConfigFilePath, readConfigFromPackageJson, getDefaultInitAnswer,
  askInitOptions, askRemoveFiles, removing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getCwd() already returns INIT_CWD when USE_INIT_CWD=true, but
path.resolve() calls throughout the codebase were still anchored to
the real process.cwd() (the package root when running via pnpm scripts).

- posixResolve: pass getCwd() as the base directory when resolving
  relative paths so all callers automatically respect USE_INIT_CWD
- createBuildOptions: replace bare path.resolve(project) with
  path.resolve(getCwd(), project) for the same reason

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When ctix resolves the project path with getCwd() as the base, the
resulting absolute path was not forwarded to transformBundleMode,
transformCreateMode, or transformModuleMode. Each transform kept the
original relative string (e.g. 'tsconfig.json') as bundleOption.project,
which ProjectContainer then re-resolved against process.cwd() (the
package root) — loading the wrong tsconfig and pulling in hundreds of
unrelated source files.

Build a resolvedArgv with the absolute project path and pass it to
all three transform calls in the non-config-file code path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… to ExcludeContainer

Glob patterns with absolute paths (especially Windows drive-letter paths like
C:/path/to/index.ts) are not reliably matched when a cwd is also provided.
Convert the output file path to a relative path from projectDirPath using
posixRelative() before adding it to the ExcludeContainer pattern list, so
glob can resolve it correctly on all platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tput path

Three regressions introduced after 2.7.2 are fixed:

1. posixResolve: revert getCwd() usage back to path.resolve() so that
   resolving relative paths is always anchored to the real process cwd,
   not a potentially empty/unexpected INIT_CWD value that caused
   path.resolve("", "./index.ts") → "/index.ts" (EROFS on macOS, wrong
   root on Windows).

2. bundling / moduling: guard against empty output option before joining
   with exportFilename. posixJoin("", "index.ts") produces "/index.ts"
   on every platform; fall back to projectDirPath when output is empty.

3. getTsIncludeFiles: restore the tsconfig.fileNames fallback that was
   removed in 3b60d38. Without it, projects that specify no explicit
   include in their tsconfig resolve to [] and nothing runs — the exact
   Windows "0 applicable files" regression reported against 2.7.5.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hMap

Replace the per-file getCorrectCasedPath O(N*D) readdir approach with a
batched buildCorrectCasePathMap that groups paths by parent directory and
reads each directory exactly once via Promise.all, reducing I/O from
O(N*D) to O(unique dirs).

Three matching problems that would arise after case-correction are fixed:

1. project.getSourceFile(correctedPath) can return null because ts-morph
   registers files under their original (possibly wrong-cased) path.
   Fix: build a correctedPath → SourceFile map from the caseMap result
   and use it instead of project.getSourceFile() in bundling/creating.

2. ExcludeContainer inline map key mismatch: getInlineCommentedFiles
   returns filePath via sourceFile.getFilePath() (original path), so the
   ExcludeContainer #inline map had original-cased keys while isExclude()
   was called with corrected paths.
   Fix: pass rawFilePaths to getInlineCommentedFiles and remap returned
   filePath values through caseMap before passing to ExcludeContainer.

3. inlineDeclarations filePath mismatch: same root cause as (2) — the
   include.isInclude / exclude.isExclude filters received original-cased
   paths that did not match corrected-path keys in both containers.
   Fix: apply caseMap remapping before the filter chain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Convert all Korean-only comments to bilingual format (English first, Korean below)
- Applies to both inline (//) and JSDoc (/** */) comments across 13 source files
- Improves accessibility for international contributors
- Also adds variable naming guideline to PROJECT.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e names

- bundling.ts: p → rawFilePath, sf → sourceFile
- creating.ts: p → rawFilePath, sf → sourceFile
- buildCorrectCasePathMap.ts: f → file
- Fix typo in PROJECT.md coding guideline example (rawFilePaths → rawFilePath)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Restrict peerDependencies typescript to >=5 <6 to prevent TypeScript 6.x
  from being installed (>=5 was resolving to 6.x which deprecated baseUrl
  and moduleResolution:node10 as hard errors)
- Fix Buffer type incompatibility in indexWrites.ts introduced in TypeScript 5.9:
  readFile now returns Buffer which is no longer assignable to writeFile's
  parameter type; resolve by passing 'utf-8' encoding to return string instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (f1f9f44) to head (8600ffb).
⚠️ Report is 15 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##            master      #195    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           91        94     +3     
  Lines         2268      2510   +242     
  Branches       457       526    +69     
==========================================
+ Hits          2268      2510   +242     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Add Debugger.ts tests: table(), logList(), close(), logFile setter,
  enable getter, bootstrap idempotency, and stream write path
- Add getCwd.ts tests: USE_INIT_CWD/INIT_CWD env var branches
- Add IncludeContainer.ts test: empty map (map.size <= 0) branch
- Add buildCorrectCasePathMap.ts test: win32 platform label branch
- Mark genuinely unreachable defensive branches with v8 ignore comments

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@imjuni imjuni merged commit 7230fe2 into master May 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant