Nhật ký công việc cho nhiệm vụ "Thêm component tương thích Markdown nội bộ"
Chuẩn hóa HTML cũ: thay thế <p> bằng <div> khi nhập liệu
Khi di chuyển nội dung cũ, một xung đột khác giữa định dạng WYSIWYG cũ và cách dựng hình React/SSR mới đã được phát hiện. Trình soạn thảo cũ đã bọc các đoạn HTML lớn trong thẻ <p>. Trong phiên bản mới, HTML được nhập như vậy có thể nằm bên trong một đoạn văn đã tồn tại từ trước, khiến React nhận được cấu trúc HTML không hợp lệ và cảnh báo:
In HTML, <p> cannot be a descendant of <p>. This will cause a hydration error.
Nguyên nhân
Vấn đề không nằm ở phần văn bản cụ thể, mà ở ngữ nghĩa của thẻ <p>: HTML không cho phép các đoạn văn lồng nhau. WYSIWYG cũ đã sử dụng <p> làm thẻ bao bọc khối đa năng, mặc dù một vùng chứa trung tính như <div> an toàn hơn cho nội dung lồng nhau tùy ý.
Điều này đặc biệt phiền toái trong SSR: trình duyệt có thể tự sửa cấu trúc HTML không hợp lệ theo cách mà React không mong đợi, và cấu trúc DOM kết quả sau khi phân tích cú pháp sẽ khác với định dạng phía máyδ chủ. Điều này dẫn đến lỗi/sai lệch kết quả hiển thị (hydration mismatch/error).
Giải pháp được chọn
Một hàm hỗ trợ (helper) sử dụng cheerio đã được thêm vào trình nhập liệu, giúp thay thế tất cả các thẻ <p> thành <div> trước khi lưu HTML cũ, đồng thời giữ nguyên HTML bên trong và các thuộc tính:
import * as cheerio from 'cheerio'
export function replacePWithDiv(html: string): string {
const $ = cheerio.load(html, { xml: false }, false)
$('p').each((_, el) => {
const $el = $(el)
const div = $('<div></div>')
div.html($el.html() || '')
for (const attr of el.attributes) {
div.attr(attr.name, attr.value)
}
$el.replaceWith(div)
})
return $.html()
}
Tại sao quá trình chuẩn hóa được thực hiện trong trình nhập liệu
Đây là vấn đề đặc thù của dữ liệu nguồn cũ (legacy), vì vậy sẽ an toàn hơn nếu sửa nó ngay tại đầu vào của hệ thống mới, thay vì bắt trình dựng hình Markdown/React chung phải liên tục bù đắp cho định dạng WYSIWYG cũ.
Trình nhập liệu ở đây đóng vai trò là lớp chuẩn hóa (normalization layer): giữ nguyên ý nghĩa của nội dung cũ, nhưng loại bỏ cấu trúc chắc chắn gây xung đột với HTML hợp lệ và quá trình hydration của React.
Ý nghĩa đối với tác vụ Markdown chung
Trường hợp này bổ sung cho vấn đề khoảng trắng/thụt lề (whitespace/indentation): đường ống (pipeline) xử lý mới phải tính đến không chỉ cú pháp Markdown, mà cả chất lượng của HTML được nhúng. Ngay cả khi Markdown về mặt hình thức là giống nhau, HTML cũ bên trong nó có thể có cấu trúc mà các chuỗi trình phân tích cú pháp/dựng hình (parser/renderer) khác nhau xử lý theo các cách khác nhau.
Giải pháp cục bộ này vẫn là một phần của lớp tương thích BiznesHelper cho đến khi một đường ống chuẩn hóa/kiểm tra tính hợp lệ tổng quát hơn xuất hiện trong haih-agent.
Trước khi tái cấu trúc Markdown trong core haih-agent, hãy thêm vào BiznesHelper một renderer nội bộ tương thích với định dạng Markdown thực tế được lưu bởi MDXEditor.