返回 oh-my-ppt
template-skeleton-validator.ts
根目录 / src / main / presentation / templates / template-skeleton-validator.ts
1 import * as cheerio from 'cheerio'
2
3 const NON_TEMPLATE_SKELETON_RESOURCE_RE =
4 /(?:^|\/)(?:(?:tailwindcss\.v3|anime\.v4|ppt-runtime|chart\.v4|katex(?:\.min)?|katex-auto-render\.min)\.(?:js|css)|assets\/fonts\/.+)(?:[?#].*)?$/i
5 const SKELETON_HINT_RE =
6 /\b(?:bg-|background|decor|decoration|texture|mask|overlay|ornament|pattern|backdrop)\b/i
7
8 const normalizeTemplateResourceRef = (value: string): string | null => {
9 const raw = value.trim().replace(/^['"]|['"]$/g, '').trim()
10 if (!raw || raw.startsWith('#')) return null
11 const withoutQuery = raw.split('#')[0].split('?')[0].trim()
12 if (
13 !withoutQuery ||
14 /^data:/i.test(withoutQuery) ||
15 /^blob:/i.test(withoutQuery) ||
16 /^javascript:/i.test(withoutQuery)
17 ) {
18 return null
19 }
20 if (NON_TEMPLATE_SKELETON_RESOURCE_RE.test(withoutQuery)) return null
21 return withoutQuery.replace(/^\.\//, '')
22 }
23
24 const collectTemplateSkeletonResourceRefs = (html: string): string[] => {
25 const refs = new Set<string>()
26 const push = (value: string | undefined | null): void => {
27 if (!value) return
28 const normalized = normalizeTemplateResourceRef(value)
29 if (normalized) refs.add(normalized)
30 }
31
32 const urlRe = /url\(\s*(['"]?)([^'")]+)\1\s*\)/gi
33 let match: RegExpExecArray | null
34 while ((match = urlRe.exec(html)) !== null) {
35 push(match[2])
36 }
37
38 try {
39 const $ = cheerio.load(html, { scriptingEnabled: false })
40 $('image').each((_, node) => {
41 const el = $(node)
42 push(el.attr('href') || el.attr('xlink:href'))
43 })
44 $('img, video, source').each((_, node) => {
45 const el = $(node)
46 const identity = [
47 el.attr('class') || '',
48 el.attr('style') || '',
49 el.parent().attr('class') || '',
50 el.parent().attr('style') || ''
51 ].join(' ')
52 if (!SKELETON_HINT_RE.test(identity)) return
53 push(el.attr('src') || el.attr('poster'))
54 })
55 } catch {
56 // CSS url(...) extraction above still covers the most important template resources.
57 }
58
59 return Array.from(refs).sort()
60 }
61
62 export const validateTemplateSkeletonPreserved = (beforeHtml: string, afterHtml: string): string[] => {
63 const beforeRefs = collectTemplateSkeletonResourceRefs(beforeHtml)
64 if (beforeRefs.length === 0) return []
65 const afterRefs = new Set(collectTemplateSkeletonResourceRefs(afterHtml))
66 return beforeRefs.filter((ref) => !afterRefs.has(ref))
67 }
68
68 lines TYPESCRIPT