MCPcopy
hub / github.com/claude-code-best/claude-code / copyDir

Function copyDir

src/utils/plugins/pluginLoader.ts:294–349  ·  view source on GitHub ↗
(src: string, dest: string)

Source from the content-addressed store, hash-verified

292 * Exported for testing purposes.
293 */
294export async function copyDir(src: string, dest: string): Promise<void> {
295 await getFsImplementation().mkdir(dest)
296
297 const entries = await readdir(src, { withFileTypes: true })
298
299 for (const entry of entries) {
300 const srcPath = join(src, entry.name)
301 const destPath = join(dest, entry.name)
302
303 if (entry.isDirectory()) {
304 await copyDir(srcPath, destPath)
305 } else if (entry.isFile()) {
306 await copyFile(srcPath, destPath)
307 } else if (entry.isSymbolicLink()) {
308 const linkTarget = await readlink(srcPath)
309
310 // Resolve the symlink to get the actual target path
311 // This prevents circular symlinks when src and dest overlap (e.g., via symlink chains)
312 let resolvedTarget: string
313 try {
314 resolvedTarget = await realpath(srcPath)
315 } catch {
316 // Broken symlink - copy the raw link target as-is
317 await symlink(linkTarget, destPath)
318 continue
319 }
320
321 // Resolve the source directory to handle symlinked source dirs
322 let resolvedSrc: string
323 try {
324 resolvedSrc = await realpath(src)
325 } catch {
326 resolvedSrc = src
327 }
328
329 // Check if target is within the source tree (using proper path prefix matching)
330 const srcPrefix = resolvedSrc.endsWith(sep)
331 ? resolvedSrc
332 : resolvedSrc + sep
333 if (
334 resolvedTarget.startsWith(srcPrefix) ||
335 resolvedTarget === resolvedSrc
336 ) {
337 // Target is within source tree - create relative symlink that preserves
338 // the same structure in the destination
339 const targetRelativeToSrc = relative(resolvedSrc, resolvedTarget)
340 const destTargetPath = join(dest, targetRelativeToSrc)
341 const relativeLinkPath = relative(dirname(destPath), destTargetPath)
342 await symlink(relativeLinkPath, destPath)
343 } else {
344 // Target is outside source tree - use absolute resolved path
345 await symlink(resolvedTarget, destPath)
346 }
347 }
348 }
349}
350
351/**

Callers 3

installFromNpmFunction · 0.85
installFromLocalFunction · 0.85

Calls 2

getFsImplementationFunction · 0.85
readdirFunction · 0.85

Tested by

no test coverage detected