源码
packages/ui/src/preview/path.ts
// Adapted from ZCode path.ts (Apache-2.0), commit 872ad960.
// See licenses/third-party-notices.txt and docs/internals/previews.md.
const WINDOWS_ABSOLUTE_PATH_RE = /^[a-zA-Z]:[\\/]/;
const UNC_PATH_RE = /^\\\\/;
const URI_ESCAPE_RE = /%[0-9A-Fa-f]{2}/;
export function getPathLeaf(path: string): string {
const normalizedPath = path.replace(/\\/g, "/").replace(/\/+$/, "");
const segments = normalizedPath.split("/").filter(Boolean);
return segments[segments.length - 1] ?? path;
}
export function isAbsoluteFilePath(path: string): boolean {
return path.startsWith("/") || WINDOWS_ABSOLUTE_PATH_RE.test(path) || UNC_PATH_RE.test(path);
}
export function decodeFilePathUriEscapes(path: string): string {
if (!URI_ESCAPE_RE.test(path)) {
return path;
}
try {
return decodeURIComponent(path);
} catch {
return path;
}
}
export function parseFileUrlPath(value: string): string | null {
try {
const url = new URL(value);
if (url.protocol !== "file:") return null;
const pathname = decodeFilePathUriEscapes(url.pathname);
if (/^\/[a-zA-Z]:\//.test(pathname)) return pathname.slice(1);
if (url.hostname && url.hostname !== "localhost") return `//${url.hostname}${pathname}`;
return pathname;
} catch {
return null;
}
}
export function normalizeWorkspaceRelativeFilePath(relativePath: string): string | null {
const segments: string[] = [];
for (const segment of relativePath.replace(/\\/g, "/").split("/")) {
if (!segment || segment === ".") continue;
if (segment === "..") {
if (segments.length === 0) return null;
segments.pop();
continue;
}
segments.push(segment);
}
return segments.join("/");
}
export function joinFilePath(basePath: string, childPath: string): string {
if (!childPath) {
return basePath;
}
if (isAbsoluteFilePath(childPath)) {
return childPath;
}
const separator = basePath.includes("\\") && !basePath.includes("/") ? "\\" : "/";
const normalizedBasePath = basePath.replace(/[\\/]+$/, "");
const normalizedChildPath = childPath.replace(/^[\\/]+/, "");
return `${normalizedBasePath}${separator}${normalizedChildPath}`;
}
function encodeUriPathForFileUrl(value: string): string {
return encodeURI(value).replace(/#/g, "%23").replace(/\?/g, "%3F");
}
export function toFileUrl(path: string): string {
const normalizedPath = path.replace(/\\/g, "/");
if (WINDOWS_ABSOLUTE_PATH_RE.test(path)) {
return `file:///${encodeUriPathForFileUrl(normalizedPath)}`;
}
if (normalizedPath.startsWith("/")) {
return `file://${encodeUriPathForFileUrl(normalizedPath)}`;
}
if (UNC_PATH_RE.test(path)) {
return `file:${encodeUriPathForFileUrl(normalizedPath)}`;
}
return encodeUriPathForFileUrl(normalizedPath);
}