refactor: 專案整理階段一——確認缺陷修復(jinze 冪等/pill 自身 avoid)+死代碼清理
下游審計(4 路並行測繪 + 逐條源碼複驗)之階段一:落地已複驗確認之缺陷與低風險 清理;架構統一(透明判定單一真相源)與文檔重整留待階段二/三。 修復(複驗確認): ・jinze 二次 render(未 revert)產生巢狀 jz-jinze、違反 I6 冪等——守衛改判 parent 自身即本 pass 產物(isJz(parent, PASS)),已綁定內容不再重包(實測 jz-jinze 3→6 已修)。 ・processPills 漏檢 avoidsSelf——自身命中 avoid(如 <code data-jz-skip>)之 pill 仍被補 間隙(isAvoided 只查祖先、不含自身,同「下游問題六」型)。processPills + root 自處理 分支補 avoidsSelf 檢查。 ・三分類互斥:CODE/KBD 自 DEFAULT_SKIP_TAGS 移除——二者已由預設 pillSelector "code, kbd" 以 pill 機制涵蓋(內部不可見+兩端補隙+斷 run/相鄰),再列 skipTags 致 同元素既 pill 又 avoid、pill 兩端間隙被 avoid 抹掉(違反互斥,且令上一項修復誤傷普通 code)。移除後普通 code 為純 pill、正常補隙;唯自身 data-jz-skip 者不補(即上項修復)。 清理: ・刪零引用死代碼 createMarker/ANY_CJK/BD_ALL/BD_CLASS_NAMES/CreateOptions.attrs; isJz 由 jinze 冪等守衛啟用、脫離死代碼。 ・死旗標 biaodian/emphasis/ruby 補 @reserved 註記(比照 hanging;點明與 charClass.biaodian 撞名異義,避免混淆)。 ・JzKind 補註 data-jz-kind 第三值 "splitoff"(split.ts 直接設、不經 createJz); longWord/orphan 冗餘 ===false 特判簡化(feature() 已對 false 回 false)。 複驗後重新定性(不照單全收): ・jinze 跨透明元素邊界避頭尾漏綁(你好<strong>。世界</strong> 之句末點號漏綁)——**真 缺陷**,但正解須改 jinze 之父級處理模型(穿越透明包裹上攀評估跨層綁定),與階段二 「透明判定單一真相源」一併處理更穩;forbiddenBreak 下探草稿已還原、缺口以註解標記。 ・lineedge edgeRect 攀穿 isolate——**非缺陷**:edgeRect 為物理版面測量(getClientRects), isolate 斷的是邏輯 run/相鄰,其內部字元之物理行首/行尾判定須跨 <mark> 找真實相鄰 內容(否則「文字<mark>「引」</mark>」之 「 誤判行首而錯收左半)。初評誤判、已還原並補註。 新增 3 回歸測試(jinze 冪等、預設 code 為 pill 非 skipTags、自身 avoid 之 pill 不補隙); 119 測試全綠、tsc + esbuild 構建乾淨。
This commit is contained in:
Vendored
-4
@@ -6,13 +6,9 @@ export interface CreateOptions {
|
||||
classes?: string | string[];
|
||||
/** 文本內容。 */
|
||||
text?: string;
|
||||
/** 其餘屬性。 */
|
||||
attrs?: Record<string, string>;
|
||||
}
|
||||
/** 造一個帶 pass 標記之 jz-* 元素。 */
|
||||
export declare function createJz(tag: JzTag, pass: string, kind: JzKind, opts?: CreateOptions): HTMLElement;
|
||||
/** 造一個 copy-clean 間隙標記(內含真實空白,user-select 由 CSS 關閉)。 */
|
||||
export declare function createMarker(tag: JzTag, pass: string): HTMLElement;
|
||||
/**
|
||||
* 還原某 pass 之全部產物(I6)。以 data-jz 定址,逆文件序處理(先內層後
|
||||
* 外層),marker 移除、wrap 解包,最後 normalize 受影響父節點合併文本。
|
||||
|
||||
Vendored
-6
@@ -5,8 +5,6 @@ export declare const CJK: string;
|
||||
export declare const ALNUM = "A-Za-z0-9";
|
||||
/** 西文字母。 */
|
||||
export declare const ALPHA = "A-Za-z";
|
||||
/** 任一 CJK 字元測試。 */
|
||||
export declare const ANY_CJK: RegExp;
|
||||
/** CJK 之後可接間隙之西文集(含 @)。 */
|
||||
export declare const ANS_AFTER_CJK: string;
|
||||
/** CJK 之前可接間隙之西文集(去 @)。 */
|
||||
@@ -25,8 +23,6 @@ export declare const BD_CLOSE = "\u300D\u300F\uFF09\u300B\u3009\u3011\u3017\u301
|
||||
export declare const BD_MIDDLE = "\u00B7\u30FB\u2027";
|
||||
/** 連續省略/破折:…—。 */
|
||||
export declare const BD_LIGA = "\u2026\u2014";
|
||||
/** 全體標點字元(供 charify 走訪偵測)。 */
|
||||
export declare const BD_ALL: string;
|
||||
/** 全寬標點之六子類鍵。 */
|
||||
export type BdSubclass = "stop" | "pause" | "open" | "close" | "middle" | "liga";
|
||||
/**
|
||||
@@ -66,8 +62,6 @@ export declare function resolveRuleset(override?: CharClassOverride): Ruleset;
|
||||
* biaodian 取自規則集(預設 DEFAULT_RULESET.biaodian,向下兼容無參數呼叫)。
|
||||
*/
|
||||
export declare function classifyBiaodian(ch: string, biaodian?: Record<BdSubclass, string>): string | null;
|
||||
/** 全體 bd-* 子類名(DOM 端讀取 jz-char 之 bd class 用)。 */
|
||||
export declare const BD_CLASS_NAMES: readonly ["bd-stop", "bd-pause", "bd-open", "bd-close", "bd-middle", "bd-liga"];
|
||||
/**
|
||||
* 標點之「可壓縮空白」側(橫排)。字面偏左者空白在右(句內/句末/閉類),
|
||||
* 開類字面偏右、空白在左。間隔號/連接號不壓縮。擠壓即收掉對應側之半形空白。
|
||||
|
||||
Vendored
+18
-16
@@ -502,14 +502,6 @@ var Juzhen = (() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.attrs) {
|
||||
for (const k in opts.attrs) {
|
||||
const v = opts.attrs[k];
|
||||
if (v !== void 0) {
|
||||
el.setAttribute(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.text !== void 0) {
|
||||
el.textContent = opts.text;
|
||||
}
|
||||
@@ -579,6 +571,16 @@ var Juzhen = (() => {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function isJz(node, pass) {
|
||||
if (!node || node.nodeType !== 1) {
|
||||
return false;
|
||||
}
|
||||
const el = node;
|
||||
if (!el.hasAttribute(PASS_ATTR)) {
|
||||
return false;
|
||||
}
|
||||
return pass === void 0 || el.getAttribute(PASS_ATTR) === pass;
|
||||
}
|
||||
|
||||
// src/core/locale.ts
|
||||
var DEFAULT_POLICY = {
|
||||
@@ -640,7 +642,6 @@ var Juzhen = (() => {
|
||||
var CJK = "\u2E80-\u2EFF\u2F00-\u2FDF\u3040-\u309F\u30A0-\u30FA\u30FC-\u30FF\u3100-\u312F\u3200-\u32FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF";
|
||||
var ALNUM = "A-Za-z0-9";
|
||||
var ALPHA = "A-Za-z";
|
||||
var ANY_CJK = new RegExp("[" + CJK + "]");
|
||||
var WEST_SHARED = "0-9\\$%\\^&\\*\\-\\+\\\\=\\u0370-\\u03ff\\u0100-\\u024f\\u0400-\\u04ff\\u00a1-\\u00b6\\u00b8-\\u00ff\\u20a0-\\u20bf\\u2150-\\u21ff\\u2200-\\u22ff\\u2700-\\u27bf";
|
||||
var ANS_AFTER_CJK = ALPHA + "@" + WEST_SHARED;
|
||||
var ANS_BEFORE_CJK = ALPHA + WEST_SHARED;
|
||||
@@ -650,7 +651,6 @@ var Juzhen = (() => {
|
||||
var BD_CLOSE = "\u300D\u300F\uFF09\u300B\u3009\u3011\u3017\u3015\uFF3D\uFF5D\u201D\u2019";
|
||||
var BD_MIDDLE = "\xB7\u30FB\u2027";
|
||||
var BD_LIGA = "\u2026\u2014";
|
||||
var BD_ALL = BD_STOP + BD_PAUSE + BD_OPEN + BD_CLOSE + BD_MIDDLE + BD_LIGA;
|
||||
var BD_ORDER = [
|
||||
"stop",
|
||||
"pause",
|
||||
@@ -1124,6 +1124,9 @@ var Juzhen = (() => {
|
||||
}
|
||||
}
|
||||
function processParent(parent, finder, anyCjk) {
|
||||
if (isJz(parent, PASS2)) {
|
||||
return;
|
||||
}
|
||||
for (let c = parent.firstChild; c; c = c.nextSibling) {
|
||||
if (c.nodeType === 1 && c.nodeName === "JZ-JINZE") {
|
||||
return;
|
||||
@@ -1752,7 +1755,7 @@ var Juzhen = (() => {
|
||||
}
|
||||
function processPills(root, finder, boundarySelector, pillNeighbor) {
|
||||
root.querySelectorAll(boundarySelector).forEach((pill) => {
|
||||
if (finder.isAvoided(pill) || finder.insidePill(pill) || !finder.inScope(pill)) {
|
||||
if (finder.avoidsSelf(pill) || finder.isAvoided(pill) || finder.insidePill(pill) || !finder.inScope(pill)) {
|
||||
return;
|
||||
}
|
||||
if (!finder.featureEnabledFor(pill, PASS5)) {
|
||||
@@ -1814,7 +1817,7 @@ var Juzhen = (() => {
|
||||
}
|
||||
processBlock2(block, finder, R);
|
||||
});
|
||||
if (!options.finder.blockTags.has(ctx.root.nodeName) && finder.featureEnabledFor(ctx.root, PASS5) && finder.inScope(ctx.root) && !finder.isAvoided(ctx.root)) {
|
||||
if (!options.finder.blockTags.has(ctx.root.nodeName) && finder.featureEnabledFor(ctx.root, PASS5) && finder.inScope(ctx.root) && !finder.avoidsSelf(ctx.root) && !finder.isAvoided(ctx.root)) {
|
||||
processBlock2(ctx.root, finder, R);
|
||||
}
|
||||
const boundarySelector = [options.finder.pillSelector, options.finder.isolateSelector].filter(Boolean).join(", ");
|
||||
@@ -1904,8 +1907,6 @@ var Juzhen = (() => {
|
||||
"HR"
|
||||
];
|
||||
var DEFAULT_SKIP_TAGS = [
|
||||
"CODE",
|
||||
"KBD",
|
||||
"PRE",
|
||||
"SAMP",
|
||||
"TT",
|
||||
@@ -1974,10 +1975,11 @@ var Juzhen = (() => {
|
||||
},
|
||||
features: {
|
||||
spacing: feature(opts.spacing, true),
|
||||
longWord: opts.longWord === false ? false : feature(opts.longWord, true),
|
||||
// feature(v,dflt) 對 v===false 即回 false,故無需 ===false 特判(與 jiya/jinze 同構)。
|
||||
longWord: feature(opts.longWord, true),
|
||||
jiya: feature(opts.jiya, true),
|
||||
jinze: feature(opts.jinze, true),
|
||||
orphan: opts.orphan === false ? false : feature(opts.orphan, true),
|
||||
orphan: feature(opts.orphan, true),
|
||||
biaodian: feature(opts.biaodian, false),
|
||||
emphasis: feature(opts.emphasis, false),
|
||||
ruby: feature(opts.ruby, false)
|
||||
|
||||
Vendored
+18
-16
@@ -475,14 +475,6 @@ function createJz(tag, pass, kind, opts = {}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.attrs) {
|
||||
for (const k in opts.attrs) {
|
||||
const v = opts.attrs[k];
|
||||
if (v !== void 0) {
|
||||
el.setAttribute(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.text !== void 0) {
|
||||
el.textContent = opts.text;
|
||||
}
|
||||
@@ -552,6 +544,16 @@ function bdClassOf(el) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function isJz(node, pass) {
|
||||
if (!node || node.nodeType !== 1) {
|
||||
return false;
|
||||
}
|
||||
const el = node;
|
||||
if (!el.hasAttribute(PASS_ATTR)) {
|
||||
return false;
|
||||
}
|
||||
return pass === void 0 || el.getAttribute(PASS_ATTR) === pass;
|
||||
}
|
||||
|
||||
// src/core/locale.ts
|
||||
var DEFAULT_POLICY = {
|
||||
@@ -613,7 +615,6 @@ function resolveStyle(node, config) {
|
||||
var CJK = "\u2E80-\u2EFF\u2F00-\u2FDF\u3040-\u309F\u30A0-\u30FA\u30FC-\u30FF\u3100-\u312F\u3200-\u32FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF";
|
||||
var ALNUM = "A-Za-z0-9";
|
||||
var ALPHA = "A-Za-z";
|
||||
var ANY_CJK = new RegExp("[" + CJK + "]");
|
||||
var WEST_SHARED = "0-9\\$%\\^&\\*\\-\\+\\\\=\\u0370-\\u03ff\\u0100-\\u024f\\u0400-\\u04ff\\u00a1-\\u00b6\\u00b8-\\u00ff\\u20a0-\\u20bf\\u2150-\\u21ff\\u2200-\\u22ff\\u2700-\\u27bf";
|
||||
var ANS_AFTER_CJK = ALPHA + "@" + WEST_SHARED;
|
||||
var ANS_BEFORE_CJK = ALPHA + WEST_SHARED;
|
||||
@@ -623,7 +624,6 @@ var BD_OPEN = "\u300C\u300E\uFF08\u300A\u3008\u3010\u3016\u3014\uFF3B\uFF5B\u201
|
||||
var BD_CLOSE = "\u300D\u300F\uFF09\u300B\u3009\u3011\u3017\u3015\uFF3D\uFF5D\u201D\u2019";
|
||||
var BD_MIDDLE = "\xB7\u30FB\u2027";
|
||||
var BD_LIGA = "\u2026\u2014";
|
||||
var BD_ALL = BD_STOP + BD_PAUSE + BD_OPEN + BD_CLOSE + BD_MIDDLE + BD_LIGA;
|
||||
var BD_ORDER = [
|
||||
"stop",
|
||||
"pause",
|
||||
@@ -1097,6 +1097,9 @@ function splitBoundaries(t, needFirst, needLast, anyCjk) {
|
||||
}
|
||||
}
|
||||
function processParent(parent, finder, anyCjk) {
|
||||
if (isJz(parent, PASS2)) {
|
||||
return;
|
||||
}
|
||||
for (let c = parent.firstChild; c; c = c.nextSibling) {
|
||||
if (c.nodeType === 1 && c.nodeName === "JZ-JINZE") {
|
||||
return;
|
||||
@@ -1725,7 +1728,7 @@ function processBlock2(block, finder, R) {
|
||||
}
|
||||
function processPills(root, finder, boundarySelector, pillNeighbor) {
|
||||
root.querySelectorAll(boundarySelector).forEach((pill) => {
|
||||
if (finder.isAvoided(pill) || finder.insidePill(pill) || !finder.inScope(pill)) {
|
||||
if (finder.avoidsSelf(pill) || finder.isAvoided(pill) || finder.insidePill(pill) || !finder.inScope(pill)) {
|
||||
return;
|
||||
}
|
||||
if (!finder.featureEnabledFor(pill, PASS5)) {
|
||||
@@ -1787,7 +1790,7 @@ var spacingPass = {
|
||||
}
|
||||
processBlock2(block, finder, R);
|
||||
});
|
||||
if (!options.finder.blockTags.has(ctx.root.nodeName) && finder.featureEnabledFor(ctx.root, PASS5) && finder.inScope(ctx.root) && !finder.isAvoided(ctx.root)) {
|
||||
if (!options.finder.blockTags.has(ctx.root.nodeName) && finder.featureEnabledFor(ctx.root, PASS5) && finder.inScope(ctx.root) && !finder.avoidsSelf(ctx.root) && !finder.isAvoided(ctx.root)) {
|
||||
processBlock2(ctx.root, finder, R);
|
||||
}
|
||||
const boundarySelector = [options.finder.pillSelector, options.finder.isolateSelector].filter(Boolean).join(", ");
|
||||
@@ -1877,8 +1880,6 @@ var DEFAULT_BLOCK_TAGS = [
|
||||
"HR"
|
||||
];
|
||||
var DEFAULT_SKIP_TAGS = [
|
||||
"CODE",
|
||||
"KBD",
|
||||
"PRE",
|
||||
"SAMP",
|
||||
"TT",
|
||||
@@ -1947,10 +1948,11 @@ function normalizeOptions(opts = {}) {
|
||||
},
|
||||
features: {
|
||||
spacing: feature(opts.spacing, true),
|
||||
longWord: opts.longWord === false ? false : feature(opts.longWord, true),
|
||||
// feature(v,dflt) 對 v===false 即回 false,故無需 ===false 特判(與 jiya/jinze 同構)。
|
||||
longWord: feature(opts.longWord, true),
|
||||
jiya: feature(opts.jiya, true),
|
||||
jinze: feature(opts.jinze, true),
|
||||
orphan: opts.orphan === false ? false : feature(opts.orphan, true),
|
||||
orphan: feature(opts.orphan, true),
|
||||
biaodian: feature(opts.biaodian, false),
|
||||
emphasis: feature(opts.emphasis, false),
|
||||
ruby: feature(opts.ruby, false)
|
||||
|
||||
Vendored
+4
@@ -70,8 +70,12 @@ export interface JuzhenOptions {
|
||||
* 守恆矛盾,CSS 接觸不到斷行器),原生 hanging-punctuation 為唯一正解但僅 Safari 支援、
|
||||
* 且與本庫 inline-block 原子互斥。此選項仍被接受以保 API 相容,但**無任何效果**。 */
|
||||
hanging?: boolean;
|
||||
/** @reserved 標點修正(簡/繁正規化)——架構已預留(切片 #7),**當前無任何效果**。
|
||||
* 注意:此為功能開關,與 `charClass.biaodian`(標點子類擴充表,語義完全不同)同名,勿混。 */
|
||||
biaodian?: boolean;
|
||||
/** @reserved 著重號/裝飾——架構已預留(切片 #8),**當前無任何效果**。 */
|
||||
emphasis?: boolean;
|
||||
/** @reserved Ruby/注音——架構已預留(切片 #8),**當前無任何效果**。 */
|
||||
ruby?: boolean;
|
||||
/** 分塊功能選擇(§3.6)。 */
|
||||
blocks?: BlockRule[];
|
||||
|
||||
Reference in New Issue
Block a user