@controlplane/cli
Version:
Control Plane Corporation CLI
54 lines • 2.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolvePhysicalTarget = resolvePhysicalTarget;
const fs = require("fs");
const path = require("path");
const config_1 = require("../config");
const errors_1 = require("../errors");
// ANCHOR - Exported Functions
/**
* Resolves a symlink target to the physical path the kernel would read: it starts
* from the link's real directory and walks the target segment by segment, following
* each symlink component and taking the real parent on '..'. A missing tail resolves
* lexically from the deepest real point (a dangling link stays where it points).
* path.resolve and realpathSync both collapse '..' lexically, which disagrees with the
* kernel across a symlinked component, so neither can guard the boundary.
*
* @param {string} linkDirAbs - The link's own directory (absolute).
* @param {string} rawTarget - The raw link target as stored on disk.
* @param {string} linkRel - The link's POSIX-relative path for error messages.
* @param {number} depth - The current symlink-following depth.
* @returns {string} The physical target path.
*/
function resolvePhysicalTarget(linkDirAbs, rawTarget, linkRel, depth = 0) {
if (depth > config_1.MAX_LINK_DEPTH) {
throw new errors_1.RemoteBuildError('context', `${linkRel} resolves through too many symlinks`, 'Remove the link cycle, then re-run the build.');
}
const absolute = path.isAbsolute(rawTarget);
const startDir = fs.realpathSync(linkDirAbs);
const root = absolute ? path.parse(rawTarget).root : '';
let current = absolute ? root : startDir;
// The root is stripped before splitting so a drive-letter prefix never re-joins as a segment.
const remainder = absolute ? rawTarget.slice(root.length) : rawTarget;
for (const segment of remainder.split(/[\\/]+/)) {
if (segment === '' || segment === '.') {
continue;
}
if (segment === '..') {
current = path.dirname(current);
continue;
}
const next = path.join(current, segment);
let stat;
try {
stat = fs.lstatSync(next);
}
catch (_a) {
current = next; // missing from here on — resolve the remainder lexically
continue;
}
current = stat.isSymbolicLink() ? resolvePhysicalTarget(path.dirname(next), fs.readlinkSync(next), linkRel, depth + 1) : next;
}
return current;
}
//# sourceMappingURL=symlink.js.map