Files
t/uniCloud-alipay/cloudfunctions/uni-cms-articles/content-adapter.js
T
root 3117146281 feat: 打通 HBuilderX CLI 构建 + 修复文章闭环静态盲区
构建:
- 新增 tools/build.js:junction HBuilderX 工具链,CLI 构建 h5/mp-weixin
- vue 指向补丁版 @dcloudio/uni-h5-vue(官方 npm vue 不导出 isInSSRComponentSetup)
- 设 HX_APP_ROOT 避免退化成 H5 空壳产物;产物完整性校验

校验工具:
- 新增 check-cloud-methods.js:acorn 解析云对象方法,比对 94 处调用点
- 新增 check-android-contract.js:Kotlin 侧云对象契约校验
- audit-project.js 修 downloadFile 误报(注释未剥离);tools/ 排除出扫描
- package.json 声明此前隐式依赖的 acorn

功能:
- 补 uni-cms-articles.getPublishedArticles(安卓端依赖但此前不存在)
- 修 u-parse <audio> 引用已移除组件导致 H5 构建失败
2026-09-12 01:02:45 +08:00

171 lines
5.5 KiB
JavaScript

const crypto = require('crypto')
function clone(value) {
if (value === undefined || value === null) return value
return JSON.parse(JSON.stringify(value))
}
function asString(value) {
if (typeof value === 'string') return value.trim()
if (value && typeof value === 'object' && typeof value.src === 'string') return value.src.trim()
return ''
}
function normalizeCmsList(list) {
if (!Array.isArray(list)) return []
return list.reduce((result, source) => {
if (!source || typeof source !== 'object') return result
const item = clone(source)
const type = typeof item.type === 'string' ? item.type : ''
if (!type) return result
if (type === 'image') {
item.image = item.image && typeof item.image === 'object' ? item.image : {}
item.image.src = asString(item.image.src)
item.image.alt = typeof item.image.alt === 'string' ? item.image.alt : ''
} else if (type === 'video') {
item.video = item.video && typeof item.video === 'object' ? item.video : {}
item.video.src = asString(item.video.src)
item.video.poster = asString(item.video.poster)
item.video.duration = Number(item.video.duration) || 0
} else if (type === 'text' || type === 'title') {
item.text = typeof item.text === 'string' ? item.text : ''
item.html = typeof item.html === 'string' ? item.html : ''
item.delta = item.delta && typeof item.delta === 'object' ? item.delta : {}
} else if (type === 'vote') {
item.vote = item.vote && typeof item.vote === 'object' ? item.vote : {}
item.vote.vote_id = typeof item.vote.vote_id === 'string' && item.vote.vote_id
? item.vote.vote_id
: createVoteId()
item.vote.voteTitle = typeof item.vote.voteTitle === 'string' ? item.vote.voteTitle.trim() : ''
item.vote.voteLst = Array.isArray(item.vote.voteLst)
? item.vote.voteLst
.filter(option => option && typeof option.value === 'string' && option.value.trim())
.map(option => ({ ...option, value: option.value.trim() }))
: []
}
if (type === 'image' && !item.image.src) return result
if (type === 'video' && !item.video.src) return result
if (type === 'text' || type === 'title') {
const hasDelta = Array.isArray(item.delta.ops) && item.delta.ops.length > 0
if (!hasDelta && !item.html && !item.text) return result
}
if (type === 'vote' && item.vote.voteLst.length < 2) return result
result.push(item)
return result
}, [])
}
function createVoteId() {
return `vote_${Date.now().toString(36)}_${crypto.randomBytes(6).toString('hex')}`
}
function textOps(item) {
const delta = item && item.delta
if (delta && Array.isArray(delta.ops) && delta.ops.length) return clone(delta.ops)
if (item && item.text) return [{ insert: item.text }]
return []
}
function appendParagraphBreak(ops) {
if (!ops.length) return ops
const last = ops[ops.length - 1]
if (typeof last.insert === 'string' && last.insert.endsWith('\n')) return ops
ops.push({ insert: '\n' })
return ops
}
function cmsLstToDelta(list) {
const cmsLst = normalizeCmsList(list)
const ops = []
for (const item of cmsLst) {
if (item.type === 'text' || item.type === 'title') {
const itemOps = textOps(item)
ops.push(...itemOps)
appendParagraphBreak(ops)
continue
}
if (item.type === 'image' && item.image.src) {
ops.push({ insert: { image: item.image.src }, attributes: item.image.alt ? { alt: item.image.alt } : undefined })
ops.push({ insert: '\n' })
continue
}
if (item.type === 'video' && item.video.src) {
ops.push({
insert: { video: item.video.src },
attributes: item.video.poster ? { poster: item.video.poster } : undefined
})
ops.push({ insert: '\n' })
continue
}
if (item.type === 'vote' && item.vote.voteTitle) {
ops.push({ insert: `${item.vote.voteTitle}\n` })
}
}
return { ops: ops.map(op => {
if (op.attributes === undefined) {
const copy = { ...op }
delete copy.attributes
return copy
}
return op
}) }
}
function getArticleText(content, cmsLst) {
const values = []
if (content && Array.isArray(content.ops)) {
for (const op of content.ops) {
if (typeof op.insert === 'string') values.push(op.insert)
}
}
for (const item of normalizeCmsList(cmsLst)) {
if (item.type === 'text' || item.type === 'title') values.push(item.text || '')
if (item.type === 'vote') {
values.push(item.vote.voteTitle || '')
values.push(...item.vote.voteLst.map(option => option.value))
}
if (item.type === 'video') values.push(item.text || '')
}
return values.join('\n').trim()
}
function getArticleImages(content, cmsLst) {
const images = []
if (content && Array.isArray(content.ops)) {
for (const op of content.ops) {
if (op && op.insert && typeof op.insert.image === 'string') images.push(op.insert.image)
}
}
for (const item of normalizeCmsList(cmsLst)) {
if (item.type === 'image' && item.image.src) images.push(item.image.src)
if (item.type === 'video' && item.video.poster) images.push(item.video.poster)
}
return [...new Set(images)]
}
function hasRenderableContent(content, cmsLst) {
return Boolean(
(content && Array.isArray(content.ops) && content.ops.some(op => op && op.insert !== undefined)) ||
normalizeCmsList(cmsLst).length
)
}
module.exports = {
clone,
normalizeCmsList,
cmsLstToDelta,
createVoteId,
getArticleText,
getArticleImages,
hasRenderableContent
}