Skip to content

fix: handle shell-escaped file paths#29

Merged
frankieyan merged 3 commits intomainfrom
frankieyan/fix-escaped-paths
Jan 19, 2026
Merged

fix: handle shell-escaped file paths#29
frankieyan merged 3 commits intomainfrom
frankieyan/fix-escaped-paths

Conversation

@frankieyan
Copy link
Copy Markdown
Member

@frankieyan frankieyan commented Jan 19, 2026

Fixes an issue where --check-files and --stage-record-file fail on files with $ in their names (common in React Router/Remix dynamic route files like users.$id.tsx).

When CI tools pass file paths through shell variables, special characters get backslash-escaped. So draft.$id.tsx becomes draft.\$id.tsx. The tool was looking for the escaped path literally instead of unescaping it first.

Added filePath.replace(/\\(.)/g, '$1') in normalizeFilePaths() to strip the backslash escapes before checking if files exist.

Test plan

  • Run npm run build
  • Run node dist/index.mjs --check-files 'src/__fixtures__/sample-project/src/route.\$param.tsx' - verify it finds the file

🤖 Generated with Claude Code

File paths containing shell metacharacters (like $) get backslash-escaped
by git/CI tools, but normalizeFilePaths() didn't unescape them, causing
validateFilesExist() to fail with "File not found" errors.

This is common with React Router/Remix dynamic route files like
users.$id.tsx which become users.\$id.tsx when passed through shell
variables in CI.

Added filePath.replace(/\\(.)/g, '$1') in normalizeFilePaths() to strip
backslash escapes before checking if files exist.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@frankieyan frankieyan requested a review from a team as a code owner January 19, 2026 17:59
@frankieyan frankieyan requested review from pauloslund and removed request for a team January 19, 2026 17:59
Copy link
Copy Markdown

@doistbot-app doistbot-app bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR correctly addresses the issue of shell-escaped paths for POSIX-style inputs, allowing tools to find files with special characters like $. However, the implementation introduces a critical regression for Windows environments by treating directory separators as escape characters. This needs to be addressed before merging.

Comment thread src/source-files.mts Outdated

return filePaths.map((filePath) => {
// Unescape shell-escaped characters (e.g., \$ → $, \! → !)
const unescaped = filePath.replace(/\\(.)/g, '$1')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Windows Path Corruption

The global regex replacement /\\(.)/g will eagerly match backslashes used as path separators on Windows systems (e.g., src\utils\file.ts becomes srcutilsfile.ts). This breaks path resolution for users providing native Windows paths.

You should likely guard this logic to only run if the path does not appear to be a native Windows path (e.g., check path.sep or if the path uses backslashes as separators), or restrict the unescaping to specific target characters like \$.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a check so we only handle paths with forward-slashes. This is because tools like git bash on Windows would still provide paths with backslashes, but path.sep or platform checks would indicate it's Windows. This does have the caveat that paths without any slashes would fail this check, but something like ./$id.tsx. would work.

Only apply shell escape unescaping to paths containing forward slashes.
Paths using only backslashes (Windows native paths) are preserved to
avoid corrupting path separators.

This handles:
- Git Bash on Windows (uses forward slashes with backslash escaping)
- Windows CI (uses forward slashes from git)
- Native Windows cmd/PowerShell (uses backslashes, no shell escaping)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@frankieyan
Copy link
Copy Markdown
Member Author

@doistbot /review

Copy link
Copy Markdown

@doistbot-app doistbot-app bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR effectively solves the issue of shell-escaped paths in CI environments. However, the unescaping logic is too broad and causes a regression for Windows paths that use mixed separators (forward and backslashes) by incorrectly stripping backslashes that are intended as directory separators.

Comment thread src/source-files.mts Outdated
Restrict the unescape regex to /\\([^a-zA-Z0-9])/g to preserve mixed
paths like src/utils\file.ts which are valid on Windows.

Shells typically escape special characters ($, space, !), not letters
or digits. Windows separators are usually followed by alphanumeric
directory names, so this change prevents corruption while still
unescaping shell metacharacters.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@frankieyan frankieyan added the 👀 Show PR PR must be reviewed before or after merging label Jan 19, 2026
@frankieyan frankieyan merged commit ae78e6a into main Jan 19, 2026
2 checks passed
@frankieyan frankieyan deleted the frankieyan/fix-escaped-paths branch January 19, 2026 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

👀 Show PR PR must be reviewed before or after merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant