Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 3x 30x 17x 13x 30x 1x 12x 1x 11x 11x 25x 25x 25x 3x 22x 1x 21x 13x 8x 8x 8x 23x 23x 23x 23x 23x 16x 16x 16x 16x 15x 15x 12x 7x 5x 14x 14x 14x 18x 18x 18x 25x 21x 21x 14x 7x | /**
* PathNormalizer
* Centralized path normalization for all config paths.
* Handles tilde expansion and recursive directory search.
*/
import { join } from "node:path";
import IFileSystem from "../transpiler/types/IFileSystem";
import NodeFileSystem from "../transpiler/NodeFileSystem";
import ICliConfig from "./types/ICliConfig";
/** Default file system instance */
const defaultFs = NodeFileSystem.instance;
class PathNormalizer {
/**
* Expand ~ at the start of a path to the home directory.
* Only expands leading tilde (~/path or bare ~).
* @param path - Path that may start with ~
* @returns Path with ~ expanded to home directory
*/
static expandTilde(path: string): string {
if (!path.startsWith("~")) {
return path;
}
const home = process.env.HOME || process.env.USERPROFILE;
if (!home) {
return path;
}
if (path === "~") {
return home;
}
Eif (path.startsWith("~/")) {
return home + path.slice(1);
}
return path;
}
/**
* Expand path/** to include all subdirectories recursively.
* If path doesn't end with /**, returns the path as single-element array
* (if it exists) or empty array (if it doesn't exist).
* @param path - Path that may end with /**
* @param fs - File system abstraction for testing
* @returns Array of all directories found
*/
static expandRecursive(path: string, fs: IFileSystem = defaultFs): string[] {
const hasRecursiveSuffix = path.endsWith("/**");
const basePath = hasRecursiveSuffix ? path.slice(0, -3) : path;
if (!fs.exists(basePath)) {
return [];
}
if (!fs.isDirectory(basePath)) {
return [basePath];
}
if (!hasRecursiveSuffix) {
return [basePath];
}
// Recursively collect all subdirectories
const dirs: string[] = [basePath];
this.collectSubdirectories(basePath, dirs, fs);
return dirs;
}
/**
* Recursively collect all subdirectories into the dirs array.
* Uses a visited set to prevent symlink loops.
*/
private static collectSubdirectories(
dir: string,
dirs: string[],
fs: IFileSystem,
visited: Set<string> = new Set(),
): void {
// Get real path to detect symlink loops
const realPath = fs.realpath ? fs.realpath(dir) : dir;
Iif (visited.has(realPath)) {
return; // Skip already-visited directories (symlink loop protection)
}
visited.add(realPath);
const entries = fs.readdir(dir);
for (const entry of entries) {
const fullPath = join(dir, entry);
Eif (fs.isDirectory(fullPath)) {
// Check if this subdirectory's real path was already visited
const subRealPath = fs.realpath ? fs.realpath(fullPath) : fullPath;
if (!visited.has(subRealPath)) {
dirs.push(fullPath);
this.collectSubdirectories(fullPath, dirs, fs, visited);
}
}
}
}
/**
* Normalize a single path (tilde expansion only).
* Used for output, headerOut, basePath.
* @param path - Path to normalize
* @returns Normalized path
*/
static normalizePath(path: string): string {
if (!path) {
return path;
}
return this.expandTilde(path);
}
/**
* Normalize include paths (tilde + recursive expansion).
* Deduplicates paths to avoid redundant includes.
* @param paths - Array of paths to normalize
* @param fs - File system abstraction for testing
* @returns Flattened array of all resolved directories (deduplicated)
*/
static normalizeIncludePaths(
paths: string[],
fs: IFileSystem = defaultFs,
): string[] {
const seen = new Set<string>();
const result: string[] = [];
for (const path of paths) {
const expanded = this.expandTilde(path);
const dirs = this.expandRecursive(expanded, fs);
for (const dir of dirs) {
if (!seen.has(dir)) {
seen.add(dir);
result.push(dir);
}
}
}
return result;
}
/**
* Normalize all paths in a CLI config.
* Single entry point for all path normalization.
* @param config - CLI config with potentially unnormalized paths
* @param fs - File system abstraction for testing
* @returns New config with all paths normalized
*/
static normalizeConfig(
config: ICliConfig,
fs: IFileSystem = defaultFs,
): ICliConfig {
return {
...config,
outputPath: this.normalizePath(config.outputPath),
headerOutDir: config.headerOutDir
? this.normalizePath(config.headerOutDir)
: undefined,
basePath: config.basePath
? this.normalizePath(config.basePath)
: undefined,
includeDirs: this.normalizeIncludePaths(config.includeDirs, fs),
};
}
}
export default PathNormalizer;
|