From fd30f9b81e419a5289f72e134b8fe8c217ff468c Mon Sep 17 00:00:00 2001 From: Snowz <372492339@qq.com> Date: Fri, 12 Dec 2025 17:14:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E4=BE=9D=E8=B5=96=E8=A7=A3=E6=9E=90):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DCSS=E4=BE=9D=E8=B5=96=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=AF=B9=E4=B8=8A=E7=BA=A7=E7=9B=AE=E5=BD=95=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=B9=B6=E5=A2=9E=E5=BC=BACDN=E5=9B=9E=E9=80=80?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正CSS依赖路径中`..`上级目录的解析,确保文件正确落盘 - 针对Font Awesome的`../webfonts`场景进行特殊处理,迁移旧版本误存文件 - 增强CDN回退逻辑以支持`@scope`包名格式 - 更新.gitignore添加cache目录,修改seed.txt测试用例 - 更新README文档说明相关改进 --- .gitignore | 3 ++- README.md | 4 +++- seed.txt | 10 ++++++++- server.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9b34b70..75a9c68 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ node_modules .yarn/ dist/ build/ -*.log \ No newline at end of file +*.log +cache \ No newline at end of file diff --git a/README.md b/README.md index 2e87ef1..2e9412e 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ - 当抓取 `CSS` 文件时,会自动解析其中的 `url(...)` 引用,并尝试下载相对路径的依赖(如字体、图片等),统一保存到 `cache/css/...` 对应目录下,保持与源路径相同的层级结构。 - 这样,形如 `@font-face { src: url(fonts/element-icons.woff) }` 的引用将会在本地落盘为:`/css/.../fonts/element-icons.woff`,无需跨域请求第三方源。 - 失败的依赖抓取会被静默跳过,不影响主 `CSS` 的可用性。 +- 针对使用 `../webfonts/...` 的场景(如 Font Awesome),已修正对上级目录的处理,确保依赖文件最终位于与 `css/` 同级的 `webfonts/` 目录;旧版本误存于 `css/webfonts/` 的文件会在后续抓取时自动迁移到正确位置。 ## 去重策略 @@ -186,4 +187,5 @@ - 增强缓存管理:分页加载(20–50/页)、按名称/类型/更新时间过滤、按名称/大小/时间排序、轻量虚拟滚动与懒加载、元数据解析(库名/版本/扩展名/类别) - 修复分段筛选:切换 `CSS/JS/全部` 时重置分页并重新加载 - 静态缓存优化:`/` 与 HTML 响应禁用缓存;为首页 CSS/JS 增加版本参数以避免浏览器缓存旧样式与脚本 - - 页面视觉细节:为 `header` 与 `main` 增加间距(≥30px),背景设置 `background-attachment: fixed` 并覆盖视窗(居中、等比、无重复) +- 页面视觉细节:为 `header` 与 `main` 增加间距(≥30px),背景设置 `background-attachment: fixed` 并覆盖视窗(居中、等比、无重复) +- 修复:CSS 依赖路径对 `..` 上级目录的正确解析与落盘(兼容 Font Awesome 的 `../webfonts`);增强 CDN 回退匹配支持 `@scope` 包名(jsDelivr / unpkg) diff --git a/seed.txt b/seed.txt index 2478f51..e82d76d 100644 --- a/seed.txt +++ b/seed.txt @@ -7,4 +7,12 @@ https://cdn.jsdmirror.com/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js https://cdn.tailwindcss.com # Element UI 2.15.13 样式(用于验证字体依赖自动抓取) -https://cdn.jsdmirror.com/npm/element-ui@2.15.13/lib/theme-chalk/index.css +https://dist.jicelue.com/css/npm/element-ui@2.15.13/lib/theme-chalk/index.css + +# Font Awesome Free 6(验证 webfonts 自动抓取) +https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css +https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/all.min.css +https://s4.zstatic.net/ajax/libs/font-awesome/5.15.4/css/v4-shims.min.css +https://s4.zstatic.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css +https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css +https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css diff --git a/server.js b/server.js index 86461a6..ee2814d 100644 --- a/server.js +++ b/server.js @@ -195,17 +195,22 @@ async function fetchCssDependencies(baseUrl, cssBuf) { } for (const rel of refs) { - // 归一化相对路径,计算本地写入位置 - const relSafe = rel.replace(/\\+/g, '/').replace(/^\/+/, '') - const relParts = relSafe.split('/').filter(p => p && p !== '..') - const localPath = path.join(targetDir, ...relParts) + // 归一化相对路径,保留 .. 以正确定位到上级目录;去掉查询与哈希 + const relNorm = rel.replace(/\\+/g, '/').replace(/^\s+|\s+$/g, '') + const noQuery = relNorm.split('#')[0].split('?')[0] + const localPathCandidate = path.join(targetDir, noQuery) + const localPath = path.normalize(localPathCandidate) const localDir = path.dirname(localPath) + // 防越权:确保写入路径仍在 CSS_DIR 根内 + const rootResolved = path.resolve(CSS_DIR) + const resolved = path.resolve(localPath) + if (!resolved.startsWith(rootResolved)) continue if (fs.existsSync(localPath)) continue if (!fs.existsSync(localDir)) fs.mkdirSync(localDir, { recursive: true }) // 依次尝试:原始源、回退源 - const primary = new URL(rel, baseUrl).toString() - const candidates = [primary, ...buildFallbacks(primary)] + const primary = new URL(relNorm, baseUrl).toString() + const candidates = [primary, ...buildScopedFallbacks(primary)] let saved = false for (const c of candidates) { try { @@ -220,6 +225,48 @@ async function fetchCssDependencies(baseUrl, cssBuf) { // no-op } } + + // 针对旧版本误保存在 packageRoot/css/webfonts 的情况,统一迁移到 packageRoot/webfonts + try { + const packageRoot = path.dirname(targetDir) + const wrongDir = path.join(packageRoot, 'css', 'webfonts') + const correctDir = path.join(packageRoot, 'webfonts') + if (fs.existsSync(wrongDir)) { + if (!fs.existsSync(correctDir)) fs.mkdirSync(correctDir, { recursive: true }) + const entries = fs.readdirSync(wrongDir, { withFileTypes: true }) + for (const e of entries) { + if (!e.isFile()) continue + const src = path.join(wrongDir, e.name) + const dst = path.join(correctDir, e.name) + if (!fs.existsSync(dst)) fs.renameSync(src, dst) + } + } + } catch {} +} + +/** + * 针对 npm/@scope 包名的CDN回退构造(jsDelivr/unpkg) + * 兼容路径:/npm/(css/)?[@scope/]@/... + * @param {string} absUrl 绝对URL + * @returns {string[]} + */ +function buildScopedFallbacks(absUrl) { + try { + const u = new URL(absUrl) + const m = u.pathname.match(/\/(?:css\/)?npm\/(?:@([^/]+)\/)?([^/@]+)@([^/]+)\/(.+)/) + if (m) { + const scope = m[1] + const name = m[2] + const version = m[3] + const rest = m[4] + const pkg = scope ? `@${scope}/${name}@${version}` : `${name}@${version}` + return [ + `https://cdn.jsdelivr.net/npm/${pkg}/${rest}`, + `https://unpkg.com/${pkg}/${rest}` + ] + } + } catch {} + return [] } /**