UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

77 lines (76 loc) 2.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Rel = Rel; const cleanPath = (path) => { if (path === '') { return '.'; } const rooted = path.startsWith('/'); const segments = path.split('/'); const stack = []; for (const segment of segments) { if (segment === '' || segment === '.') { continue; } if (segment === '..') { const top = stack.at(-1); if (top && top !== '..') { stack.pop(); } else if (!rooted) { stack.push('..'); } continue; } stack.push(segment); } const cleaned = `${rooted ? '/' : ''}${stack.join('/')}`; if (cleaned === '') { return rooted ? '/' : '.'; } return cleaned; }; const toSegments = (cleanedPath) => { if (cleanedPath === '.' || cleanedPath === '/') { return []; } return cleanedPath.split('/').filter(Boolean); }; function Rel(basePath, targetPath) { // discuss at: https://locutus.io/golang/filepath/Rel // parity verified: Go 1.23 // original by: Kevin van Zonneveld (https://kvz.io) // note 1: Computes a lexical relative path for slash-separated inputs without consulting the current working directory. // note 2: Throws when one path is rooted and the other is relative, matching Go's error case. // example 1: Rel('a/b', 'a/b/c') // returns 1: 'c' // example 2: Rel('/a/b', '/a/c') // returns 2: '../c' // example 3: Rel('a/.', 'a/b/..') // returns 3: '.' const baseValue = String(basePath); const targetValue = String(targetPath); const cleanBase = cleanPath(baseValue); const cleanTarget = cleanPath(targetValue); const baseRooted = cleanBase.startsWith('/'); const targetRooted = cleanTarget.startsWith('/'); if (baseRooted !== targetRooted) { throw new TypeError(`Rel(): can't make ${targetValue} relative to ${baseValue}`); } const baseSegments = toSegments(cleanBase); const targetSegments = toSegments(cleanTarget); let shared = 0; while (shared < baseSegments.length && shared < targetSegments.length && baseSegments[shared] === targetSegments[shared]) { shared += 1; } if (baseSegments.slice(shared).includes('..')) { throw new TypeError(`Rel(): can't make ${targetValue} relative to ${baseValue}`); } const relativeSegments = [ ...Array.from({ length: baseSegments.length - shared }, () => '..'), ...targetSegments.slice(shared), ]; return relativeSegments.length > 0 ? relativeSegments.join('/') : '.'; }