'use strict'; const path = require('node:path'); function cloneDirectoryName(remoteUrl) { const raw = String(remoteUrl || '').trim().replace(/[?#].*$/, '').replace(/[\\/]+$/, ''); const segment = raw.split(/[\\/:]/).filter(Boolean).at(-1) || 'repository'; const name = segment.replace(/\.git$/i, '').replace(/[^a-zA-Z0-9._-]/g, '-'); // A name made only of dots is not a usable directory. Windows strips trailing // dots, so "..." would resolve back to the project root itself and slip past // the escape check in resolveCloneTarget below. return !name || /^\.+$/.test(name) ? 'repository' : name; } function resolveCloneTarget(workspaceRoot, remoteUrl) { const root = path.resolve(String(workspaceRoot || '')); if (!String(workspaceRoot || '').trim()) throw new Error('A project root is required.'); const target = path.resolve(root, cloneDirectoryName(remoteUrl)); const normalize = (value) => process.platform === 'win32' ? value.toLowerCase() : value; const normalizedRoot = normalize(root); const normalizedTarget = normalize(target); if (normalizedTarget === normalizedRoot || !normalizedTarget.startsWith(`${normalizedRoot}${path.sep}`)) { throw new Error('Clone target escapes the selected project root.'); } return { root, target, directoryName: path.basename(target) }; } module.exports = { cloneDirectoryName, resolveCloneTarget };