mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-17 22:13:08 +00:00
Fix #684: Prevent Windows reserved filename creation - Add sanitizeFilename() utility to detect and prefix Windows reserved names (NUL, CON, PRN, AUX, COM1-9, LPT1-9) - Apply sanitization to save-image route to prevent "nul" file creation - Add 23 comprehensive tests for filename sanitization edge cases Fix #576: Detect actual dev server port from output - Parse stdout/stderr for real server URLs (Vite, Next.js, generic formats) - Update server URL when detected instead of using allocated PORT - Emit dev-server:url-detected event for frontend updates - Add 6 tests for URL detection patterns Fix #193: Commit only feature-specific changes - Change from 'git add -A' to branch-aware file staging - Use git diff to find files changed on feature branch only - Prevent committing unrelated changes from other features - Maintain backward compatibility with main branch workflow All fixes include comprehensive tests and maintain backward compatibility. Test results: 1,968 tests passed (547 package + 1,421 server tests)
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
/**
|
|
* Path Utilities - Cross-platform path manipulation helpers
|
|
*
|
|
* Provides functions for normalizing and comparing file system paths
|
|
* across different operating systems (Windows, macOS, Linux).
|
|
*/
|
|
|
|
/**
|
|
* Normalize a path by converting backslashes to forward slashes
|
|
*
|
|
* This ensures consistent path representation across platforms:
|
|
* - Windows: C:\Users\foo\bar -> C:/Users/foo/bar
|
|
* - Unix: /home/foo/bar -> /home/foo/bar (unchanged)
|
|
*
|
|
* @param p - Path string to normalize
|
|
* @returns Normalized path with forward slashes
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* normalizePath("C:\\Users\\foo\\bar"); // "C:/Users/foo/bar"
|
|
* normalizePath("/home/foo/bar"); // "/home/foo/bar"
|
|
* ```
|
|
*/
|
|
export function normalizePath(p: string): string {
|
|
return p.replace(/\\/g, '/');
|
|
}
|
|
|
|
/**
|
|
* Compare two paths for equality after normalization
|
|
*
|
|
* Handles null/undefined values and normalizes paths before comparison.
|
|
* Useful for checking if two paths refer to the same location regardless
|
|
* of platform-specific path separators.
|
|
*
|
|
* @param p1 - First path to compare (or null/undefined)
|
|
* @param p2 - Second path to compare (or null/undefined)
|
|
* @returns true if paths are equal (or both null/undefined), false otherwise
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* pathsEqual("C:\\foo\\bar", "C:/foo/bar"); // true
|
|
* pathsEqual("/home/user", "/home/user"); // true
|
|
* pathsEqual("/home/user", "/home/other"); // false
|
|
* pathsEqual(null, undefined); // false
|
|
* pathsEqual(null, null); // true
|
|
* ```
|
|
*/
|
|
export function pathsEqual(p1: string | undefined | null, p2: string | undefined | null): boolean {
|
|
if (!p1 || !p2) return p1 === p2;
|
|
return normalizePath(p1) === normalizePath(p2);
|
|
}
|
|
|
|
/**
|
|
* Sanitize a filename to be safe for cross-platform file system usage
|
|
*
|
|
* Removes or replaces characters that are invalid on various file systems
|
|
* and prevents Windows reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9).
|
|
*
|
|
* @param filename - The filename to sanitize (without path, just the name)
|
|
* @param fallback - Fallback name if sanitization results in empty string (default: 'file')
|
|
* @returns A sanitized filename safe for all platforms
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* sanitizeFilename("my file.txt"); // "my_file.txt"
|
|
* sanitizeFilename("nul.txt"); // "_nul.txt" (Windows reserved)
|
|
* sanitizeFilename("con"); // "_con" (Windows reserved)
|
|
* sanitizeFilename("file?.txt"); // "file.txt"
|
|
* sanitizeFilename(""); // "file"
|
|
* sanitizeFilename("", "unnamed"); // "unnamed"
|
|
* ```
|
|
*/
|
|
export function sanitizeFilename(filename: string, fallback: string = 'file'): string {
|
|
if (!filename || typeof filename !== 'string') {
|
|
return fallback;
|
|
}
|
|
|
|
// Remove or replace invalid characters:
|
|
// - Path separators: / \
|
|
// - Windows invalid chars: : * ? " < > |
|
|
// - Control characters and other problematic chars
|
|
let safeName = filename
|
|
.replace(/[/\\:*?"<>|]/g, '') // Remove invalid chars
|
|
.replace(/\s+/g, '_') // Replace spaces with underscores
|
|
.replace(/\.+$/g, '') // Remove trailing dots (Windows issue)
|
|
.replace(/^\.+/g, '') // Remove leading dots
|
|
.trim();
|
|
|
|
// If empty after sanitization, use fallback
|
|
if (!safeName || safeName.length === 0) {
|
|
return fallback;
|
|
}
|
|
|
|
// Handle Windows reserved device names (case-insensitive)
|
|
// Reserved names: CON, PRN, AUX, NUL, COM1-9, LPT1-9
|
|
const windowsReserved = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i;
|
|
if (windowsReserved.test(safeName)) {
|
|
safeName = `_${safeName}`;
|
|
}
|
|
|
|
return safeName;
|
|
}
|