v0.10.27: /product/$slug 产品单模板一比一(模板2741/29972 补录、Woo 图廊/meta/tabs/相关产品组件、page_css 130 篇、动态按钮落地)+ css-translate 共享模块

This commit is contained in:
bigcode
2026-11-03 05:18:10 +08:00
parent d016be03ae
commit 35d154870a
9 changed files with 472 additions and 277 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* # 模块 原站页面生成 CSS 翻译(post-<id>.css → data-el 选择器)
* 供 import-page-css / import-product-css 等导入脚本共用。
*/
/** # 函数 把选择器中的 elementor-element-<id> 改写为 data-el 属性选择器
* 规则保留条件:含文档 id,或引用 .ele-<自定类>(页面自定义类);其余丢弃
* 参数 sel 原选择器;ids 文档元素 id 集合;pid 页面(模板)post id
*/
export function translateSelector(sel: string, ids: Set<string>, pid: number): string | null {
const idIn = [...sel.matchAll(/elementor-element-(\w+)/g)].map((m) => m[1]);
const hasDocId = idIn.some((id) => ids.has(id));
const hasCustomCls = /\.ele-[a-z][\w-]*/i.test(sel) && !/elementor-element-/.test(sel);
if (!hasDocId && !hasCustomCls) return null;
let out = sel;
for (const id of new Set(idIn)) {
if (!ids.has(id)) continue;
out = out.split(`.elementor-element.elementor-element-${id}`).join(`[data-el="${id}"]`);
out = out.split(`.elementor-element-${id}`).join(`[data-el="${id}"]`);
}
// 剥页面作用域前缀(.elementor-<pid> 空格相连)与多余空白
out = out.replace(new RegExp(`\\.elementor-${pid}\\s*`, "g"), "");
return out.replace(/\s+/g, " ").trim();
}
/** # 函数 解析 CSS 顶层规则(含 @media 嵌套块),只保留命中文档 id/自定类的规则
* 参数 css 原始 CSS 文本;ids 文档元素 id 集合;pid 页面 post id
*/
export function filterCssForIds(css: string, ids: Set<string>, pid: number): string {
let out = "";
let i = 0;
const n = css.length;
while (i < n) {
// 跳过注释
if (css.startsWith("/*", i)) {
const end = css.indexOf("*/", i + 2);
i = end === -1 ? n : end + 2;
continue;
}
const brace = css.indexOf("{", i);
if (brace === -1) break;
const sel = css.slice(i, brace).trim();
let depth = 1;
let j = brace + 1;
while (j < n && depth > 0) {
if (css[j] === "{") depth++;
else if (css[j] === "}") depth--;
j++;
}
const body = css.slice(brace + 1, j - 1);
if (sel.startsWith("@media") || sel.startsWith("@supports")) {
const inner = filterCssForIds(body, ids, pid);
if (inner) out += `${sel}{${inner}}\n`;
} else if (sel.startsWith("@keyframes") || sel.startsWith("@font-face")) {
out += `${sel}{${body}}\n`;
} else {
const parts = sel.split(",").map((p) => translateSelector(p, ids, pid)).filter((p): p is string => !!p);
if (parts.length) out += `${parts.join(",")}{${body}}\n`;
}
i = j;
}
return out;
}
/** # 函数 从文档(数组或 {elements} 包装)提取全部元素 id 集合 */
export function collectDocIds(el: unknown, out: Set<string> = new Set()): Set<string> {
if (Array.isArray(el)) {
for (const x of el) collectDocIds(x, out);
return out;
}
if (!el || typeof el !== "object") return out;
const e = el as { id?: unknown; elements?: unknown[] };
if (typeof e.id === "string") out.add(e.id);
for (const c of Array.isArray(e.elements) ? e.elements : []) collectDocIds(c, out);
return out;
}