@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
97 lines (96 loc) • 4.05 kB
JavaScript
export function relateUrl(from, to) {
// Normalize paths by removing multiple slashes and ensuring leading slash
function normalizePath(path) {
const match = path.match(/^(?:[a-zA-Z]+:\/\/[^\/]+)?(.*)$/) || [];
return '/' + (match[1] || path).replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '/');
}
// Check if 'to' is an absolute URL with a different domain
const fromMatch = from.match(/^([a-zA-Z]+:\/\/[^\/]+)/);
const toMatch = to.match(/^([a-zA-Z]+:\/\/[^\/]+)/);
if (toMatch && (!fromMatch || fromMatch[1] !== toMatch[1])) {
return to; // Return absolute URL if domains differ
}
// Split paths into segments
const fromSegments = normalizePath(from).split('/');
const toSegments = normalizePath(to).split('/');
// Remove empty segments
fromSegments.shift();
toSegments.shift();
// If 'from' is a file, remove the file name to get its directory
if (fromSegments.length > 0 && fromSegments[fromSegments.length - 1].includes('.')) {
fromSegments.pop(); // Remove file name
}
// Find common prefix
let commonLength = 0;
while (commonLength < fromSegments.length &&
commonLength < toSegments.length &&
fromSegments[commonLength] === toSegments[commonLength]) {
commonLength++;
}
// Calculate number of parent directories needed
const upCount = fromSegments.length - commonLength;
const relativeSegments = toSegments.slice(commonLength);
// Build relative path
let relativePath = '';
if (upCount > 0 || relativeSegments.length > 0) {
// Add parent directory references
relativePath = '../'.repeat(upCount);
// Add remaining segments
relativePath += relativeSegments.join('/');
}
else {
// Same directory
relativePath = './';
}
// Handle case when to path is empty or root
if (to === '' || to === '/' || (toMatch && to === toMatch[1] + '/')) {
relativePath = '/';
}
return relativePath;
}
export function absolutizeUrl(fullPath, relativePath) {
// Normalize paths by removing multiple slashes and ensuring leading slash
function normalizePath(path) {
return '/' + path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '/');
}
// Parse URL to separate protocol, host, and path
const urlRegex = /^(https?:\/\/[^\/]+)(\/.*)?$/;
const match = fullPath.match(urlRegex);
if (!match) {
throw new Error('Invalid fullPath URL');
}
const baseUrl = match[1];
let basePath = match[2] || '/';
// Handle absolute URLs or root paths
if (relativePath.match(/^[a-zA-Z]+:\/\//)) {
return relativePath; // Return as-is if it's a full URL
}
// If relativePath starts with '/', treat it as an absolute path from the base URL
if (relativePath.startsWith('/')) {
return baseUrl + normalizePath(relativePath);
}
// Split basePath into segments and remove the file name if it's a path to a file
const baseSegments = normalizePath(basePath).split('/');
baseSegments.shift(); // Remove empty segment from leading /
// If basePath is a file, remove the file name to treat it as a directory
if (basePath.match(/\.[a-zA-Z0-9]+$/)) {
baseSegments.pop(); // Remove the last segment if basePath points to a file
}
// Process relative path segments
const relativeSegments = relativePath.split('/');
const resultSegments = [...baseSegments];
for (const segment of relativeSegments) {
if (segment === '' || segment === '.') {
continue; // Skip empty segments or '.' (current directory)
}
else if (segment === '..') {
resultSegments.pop(); // Move up one directory for '..'
}
else {
resultSegments.push(segment); // Add the segment to the result path
}
}
// Construct absolute path
const absolutePath = normalizePath('/' + resultSegments.join('/'));
return baseUrl + absolutePath;
}