Files
geo/apps/desktop-client/src/main/adapters/dongchedi-content.ts
T
root 162abdc97c
Backend CI / Backend (push) Has been cancelled
Frontend CI / Frontend (push) Failing after 1m39s
chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports)
- Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier
- Add root scripts: format, format:check, lint, lint:fix
- Reformat 257 files across admin-web, ops-web, desktop-client, packages
- Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters
- Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex
- Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:39:09 +08:00

115 lines
3.8 KiB
TypeScript

import { renderTablesAsParagraphRows } from '../../../../../packages/publisher-platforms/src/baijiahao'
export function prepareDongchediArticleHtml(html: string, title = ''): string {
let next = renderTablesAsParagraphRows(html).trim()
next = next.replace(/<hr\b[^>]*\/?>/gi, '')
next = normalizeDongchediImageTags(next)
next = next.replace(/<h[1-6]\b[^>]*>([\s\S]*?)<\/h[1-6]>/gi, (_match, content: string) => {
const text = normalizeInlineContent(content)
return text ? `<p><strong>${text}</strong></p>` : ''
})
next = next.replace(/<li\b[^>]*>([\s\S]*?)<\/li>/gi, (_match, content: string) => {
const text = normalizeInlineContent(content)
return text ? `<p>${text}</p>` : ''
})
next = next.replace(/<\/?(?:ol|ul)\b[^>]*>/gi, '')
next = next.replace(/<p\b[^>]*>/gi, '<p>')
next = next.replace(/<(strong|b)\b[^>]*>/gi, '<strong>').replace(/<\/b>/gi, '</strong>')
next = next.replace(/<br\s*\/?>/gi, ' ')
next = next.replace(/<(?!\/?(?:p|strong|img)\b)[^>]+>/gi, '')
next = next.replace(/<p>\s*<\/p>/gi, '')
next = next.replace(/\s+/g, ' ')
next = next.replace(/>\s+</g, '><')
next = stripLeadingDuplicateTitleParagraph(next, title)
return next.trim()
}
export function countDongchediContentWordCount(html: string): number {
const text = decodeDongchediHtmlEntities(html)
.replace(/<[^>]*>/g, '')
.replace(/\s+/g, '')
.trim()
return [...text].length
}
function decodeDongchediHtmlEntities(value: string): string {
return value
.replace(/&nbsp;/gi, ' ')
.replace(/&amp;/gi, '&')
.replace(/&lt;/gi, '<')
.replace(/&gt;/gi, '>')
.replace(/&quot;/gi, '"')
.replace(/&#39;|&apos;/gi, "'")
.replace(/&#(\d+);/g, (_match, code: string) => {
const value = Number.parseInt(code, 10)
return Number.isFinite(value) && value >= 0 && value <= 0x10ffff
? String.fromCodePoint(value)
: ''
})
.replace(/&#x([0-9a-f]+);/gi, (_match, code: string) => {
const value = Number.parseInt(code, 16)
return Number.isFinite(value) && value >= 0 && value <= 0x10ffff
? String.fromCodePoint(value)
: ''
})
}
function normalizeDongchediImageTags(html: string): string {
return html.replace(/<img\b[^>]*>/gi, (imageTag: string) => {
const src = extractAttribute(imageTag, 'src')
if (!src) {
return ''
}
const alt = extractAttribute(imageTag, 'alt') ?? ''
return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}" />`
})
}
function normalizeInlineContent(value: string): string {
return value
.replace(/<br\s*\/?>/gi, ' ')
.replace(
/<\/?(?:p|div|section|article|header|footer|ol|ul|li|blockquote|h[1-6])\b[^>]*>/gi,
' ',
)
.replace(/\s+/g, ' ')
.trim()
}
function stripLeadingDuplicateTitleParagraph(html: string, title: string): string {
const normalizedTitle = normalizeComparableText(title)
if (!normalizedTitle) {
return html
}
return html.replace(/^\s*<p>([\s\S]*?)<\/p>/i, (match, paragraphContent: string) => {
return normalizeComparableText(paragraphContent) === normalizedTitle ? '' : match
})
}
function normalizeComparableText(value: string): string {
return decodeDongchediHtmlEntities(value)
.replace(/<[^>]*>/g, '')
.replace(/\s+/g, '')
.trim()
}
function extractAttribute(sourceTag: string, attributeName: string): string | null {
const matcher = new RegExp(
`\\s${attributeName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>]+))`,
'i',
)
const match = matcher.exec(sourceTag)
const value = match?.[1] ?? match?.[2] ?? match?.[3] ?? ''
const trimmed = value.trim()
return trimmed ? trimmed : null
}
function escapeAttribute(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}