const isJsSlot = (data) => {
if (typeof data !== 'object' || data === null) {
return false
}
return data?.type === 'JSSlot' && Array.isArray(data?.value)
}
const isNodeLike = (data) => {
if (typeof data !== 'object' || data === null) {
return false
}
return typeof data?.componentName === 'string' && data?.componentName
}
const MAX_PROPS_DEPTH = 1000
function traverseProps(value, depth, resRef, propsSeenRef, nodesSeenRef) {
if (depth > MAX_PROPS_DEPTH) {
return
}
if (value === null || value === undefined) {
return
}
if (typeof value !== 'object') {
return
}
if (propsSeenRef.has(value)) {
return
}
propsSeenRef.add(value)
if (isJsSlot(value)) {
const arr = value.value || []
for (const item of arr) {
if (isNodeLike(item)) {
if (item.componentType === 'Block' && typeof item.componentName === 'string' && item.componentName) {
resRef.push(item.componentName)
}
collectFromNode(item, resRef, propsSeenRef, nodesSeenRef)
} else {
traverseProps(item, depth + 1, resRef, propsSeenRef, nodesSeenRef)
}
}
return
}
if (isNodeLike(value)) {
if (value.componentType === 'Block' && typeof value.componentName === 'string' && value.componentName) {
resRef.push(value.componentName)
}
collectFromNode(value, resRef, propsSeenRef, nodesSeenRef)
return
}
if (Array.isArray(value)) {
for (const item of value) {
traverseProps(item, depth + 1, resRef, propsSeenRef, nodesSeenRef)
}
return
}
for (const key of Object.keys(value)) {
traverseProps(value[key], depth + 1, resRef, propsSeenRef, nodesSeenRef)
}
}
function collectFromNode(node, resRef, propsSeenRef, nodesSeenRef) {
if (typeof node !== 'object' || node === null) {
return
}
if (nodesSeenRef.has(node)) {
return
}
nodesSeenRef.add(node)
if (Array.isArray(node.children)) {
for (const child of node.children) {
if (typeof child !== 'object' || child === null) {
continue
}
if (child.componentType === 'Block' && typeof child.componentName === 'string' && child.componentName) {
resRef.push(child.componentName)
}
collectFromNode(child, resRef, propsSeenRef, nodesSeenRef)
}
}
traverseProps(node.props, 0, resRef, propsSeenRef, nodesSeenRef)
}
export const parseRequiredBlocks = (schema) => {
const res = []
const propsSeen = new WeakSet()
const nodesSeen = new WeakSet()
if (typeof schema !== 'object' || schema === null) {
return res
}
collectFromNode(schema, res, propsSeen, nodesSeen)
return Array.from(new Set(res))
}