67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
/**
|
|
* URL 工具链 - 处理阿里系 CDN 图片 URL
|
|
* 从 docs/extension/plan.md §6.3 移植(来自生产代码)
|
|
*/
|
|
|
|
const IMG_EXT = /\.(jpg|jpeg|png|gif|bmp|heic|webp|avif)$/i;
|
|
|
|
/**
|
|
* 缩略图 URL → 原图 URL
|
|
* 阿里 CDN 尺寸后缀在扩展名后:xxx.jpg_400x400.jpg → xxx.jpg
|
|
*/
|
|
export function toOriginalUrl(url: string): string {
|
|
const m = url.match(/^(.+?\.(jpg|jpeg|png|gif|bmp|heic|webp|avif))_/i);
|
|
return m ? m[1] : url;
|
|
}
|
|
|
|
/** url("https://...") → https://... */
|
|
export function urlInBrackets(s: string): string {
|
|
if (!s?.trim()) return '';
|
|
return s.match(/\((.*?)\)/)?.[1]?.replace(/['"]/g, '') ?? '';
|
|
}
|
|
|
|
export function isDataUrl(u: string): boolean {
|
|
return /^data:image/.test(u);
|
|
}
|
|
|
|
/** 协议相对 // / 根相对 / / 相对路径 → 绝对 URL */
|
|
export function toAbsoluteUrl(u: string): string {
|
|
if (!u) return u;
|
|
if (isDataUrl(u) || u.startsWith('blob:')) return u;
|
|
const proto = u.startsWith('http:') ? 'http' : 'https';
|
|
if (/^\/\//.test(u)) return `${proto}:${u}`;
|
|
if (/^\//.test(u)) return `${location.origin}${u}`;
|
|
if (!/^(.*):/.test(u)) return `${location.origin}/${u}`;
|
|
return u;
|
|
}
|
|
|
|
/** 去重用的归一化 key:剥掉尺寸后缀和 query */
|
|
export function dedupeKey(url: string): string {
|
|
const base = toOriginalUrl(url);
|
|
try {
|
|
const u = new URL(base);
|
|
u.search = '';
|
|
u.hash = '';
|
|
return u.toString();
|
|
} catch {
|
|
return base;
|
|
}
|
|
}
|
|
|
|
export function looksLikeImageUrl(u: string): boolean {
|
|
if (isDataUrl(u)) return true;
|
|
try {
|
|
return IMG_EXT.test(new URL(u).pathname);
|
|
} catch {
|
|
return IMG_EXT.test(u);
|
|
}
|
|
}
|
|
|
|
/** 清洗文件名非法字符(Windows 兼容) */
|
|
export function cleanFilename(name: string): string {
|
|
return name
|
|
.replace(/[<>:"/\\|?*]/g, '_')
|
|
.replace(/\s+/g, '_')
|
|
.substring(0, 80);
|
|
}
|