feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等

新增:
- 书库导入导出(library_import/library_export)及测试
- 图片变体生成(image_variants)与响应式图片(responsiveImage)
- 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件
- 小组件运行时(widgetRuntime)与静态检查(widgetLint)
- 上传缓存中间件(upload_cache)与字体 CSS 提取脚本

其它:
- 后端 handlers/services 全量调整
- 前端页面、组件、库函数与配置更新
This commit is contained in:
2026-10-01 03:06:05 +08:00
parent 7fbd6ba699
commit ff2ab286fb
141 changed files with 7527 additions and 1407 deletions

View File

@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { mergeWidgetCode, parseMixedWidgetCode, scopeWidgetCss } from "./widgetCode.ts";
import { mergeWidgetCode, parseMixedWidgetCode } from "./widgetCode.ts";
describe("parseMixedWidgetCode", () => {
it("拆分混写的 style/script 段", () => {
@@ -45,58 +45,3 @@ describe("mergeWidgetCode", () => {
assert.equal(mergeWidgetCode({ html: "hi", css: "", js: "" }), "hi");
});
});
describe("scopeWidgetCss", () => {
const p = ".j13-widget-7";
it("普通规则加前缀", () => {
assert.equal(scopeWidgetCss(".a { color: red; }", 7), `${p} .a { color: red; }`);
});
it("选择器列表逐个前缀,括号内逗号不分割", () => {
assert.equal(
scopeWidgetCss(".a, .b {}", 7),
`${p} .a, ${p} .b {}`
);
assert.equal(
scopeWidgetCss(':is(.a, .b) { color: red }', 7),
`${p} :is(.a, .b) { color: red }`
);
});
it("root/body 选择器替换为容器本身", () => {
assert.equal(scopeWidgetCss(":root { --x: 1; }", 7), `${p} { --x: 1; }`);
assert.equal(scopeWidgetCss("body { margin: 0 }", 7), `${p} { margin: 0 }`);
assert.equal(scopeWidgetCss("body .card {}", 7), `${p} .card {}`);
});
it("容器自身伪类紧贴前缀", () => {
assert.equal(scopeWidgetCss(":hover { opacity: .8 }", 7), `${p}:hover { opacity: .8 }`);
});
it("@media 内层前缀化,头部原样", () => {
assert.equal(
scopeWidgetCss("@media (min-width: 10px) { .a {} }", 7),
`@media (min-width: 10px) { ${p} .a {} }`
);
});
it("@keyframes 原样保留", () => {
const css = "@keyframes spin { from { opacity: 0 } to { opacity: 1 } }";
assert.equal(scopeWidgetCss(css, 7), css);
});
it("@import 语句原样保留", () => {
assert.equal(
scopeWidgetCss('@import url("a.css"); .a {}', 7),
'@import url("a.css"); .j13-widget-7 .a {}'
);
});
it("注释与字符串内的花括号不干扰配对", () => {
assert.equal(
scopeWidgetCss("/* { nope } */ .a::after { content: \"{\" }", 7),
`/* { nope } */ ${p} .a::after { content: "{" }`
);
});
});