js-hub / utils /symlink.ts
coyotte508's picture
coyotte508 HF Staff
Add 1 files
21dd449 verified
/**
* Heavily inspired by https://github.com/huggingface/huggingface_hub/blob/fcfd14361bd03f23f82efced1aa65a7cbfa4b922/src/huggingface_hub/file_download.py#L517
*/
import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";
function expandUser(path: string): string {
if (path.startsWith("~")) {
return path.replace("~", os.homedir());
}
return path;
}
/**
* Create a symbolic link named dst pointing to src.
*
* By default, it will try to create a symlink using a relative path. Relative paths have 2 advantages:
* - If the cache_folder is moved (example: back-up on a shared drive), relative paths within the cache folder will
* not break.
* - Relative paths seems to be better handled on Windows. Issue was reported 3 times in less than a week when
* changing from relative to absolute paths. See https://github.com/huggingface/huggingface_hub/issues/1398,
* https://github.com/huggingface/diffusers/issues/2729 and https://github.com/huggingface/transformers/pull/22228.
* NOTE: The issue with absolute paths doesn't happen on admin mode.
* When creating a symlink from the cache to a local folder, it is possible that a relative path cannot be created.
* This happens when paths are not on the same volume. In that case, we use absolute paths.
*
* The result layout looks something like
* └── [ 128] snapshots
* β”œβ”€β”€ [ 128] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f
* β”‚ β”œβ”€β”€ [ 52] README.md -> ../../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812
* β”‚ └── [ 76] pytorch_model.bin -> ../../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
*
* If symlinks cannot be created on this platform (most likely to be Windows), the workaround is to avoid symlinks by
* having the actual file in `dst`. If it is a new file (`new_blob=True`), we move it to `dst`. If it is not a new file
* (`new_blob=False`), we don't know if the blob file is already referenced elsewhere. To avoid breaking existing
* cache, the file is duplicated on the disk.
*/
export async function createSymlink(params: {
/**
* The path to the symlink.
*/
finalPath: string;
/**
* The path the symlink should point to.
*/
sourcePath: string;
}): Promise<void> {
const abs_src = path.resolve(expandUser(params.sourcePath));
const abs_dst = path.resolve(expandUser(params.finalPath));
try {
await fs.rm(abs_dst);
} catch {
// ignore
}
try {
await fs.symlink(path.relative(path.dirname(abs_dst), abs_src), abs_dst);
} catch {
console.info(`Symlink not supported. Copying file from ${abs_src} to ${abs_dst}`);
await fs.copyFile(abs_src, abs_dst);
}
}