(structure?: Protocol | Protocol[] | null, frames?: FigmaFrameInfo[])
| 234 | * @param frames - The Figma frames tree for element extraction |
| 235 | */ |
| 236 | export function postProcessStructure(structure?: Protocol | Protocol[] | null, frames?: FigmaFrameInfo[]): void { |
| 237 | if (!structure) { |
| 238 | return; |
| 239 | } |
| 240 | |
| 241 | // Utility to join alias path segments (always POSIX '/') |
| 242 | const joinSegments = (...segments: (string | undefined)[]): string => |
| 243 | path.posix.join(...segments.filter((segment): segment is string => Boolean(segment && segment.length))); |
| 244 | |
| 245 | const nodes = Array.isArray(structure) ? structure : [structure]; |
| 246 | let rootPath = '@/components'; |
| 247 | |
| 248 | // Convert component name to kebab-case for file naming |
| 249 | const toKebabName = (node: Protocol): string => { |
| 250 | const source = node.data.kebabName || node.data.name || node.id || 'component'; |
| 251 | const kebabName = toKebabCase(source); |
| 252 | if (!node.data.kebabName) { |
| 253 | node.data.kebabName = kebabName; |
| 254 | } |
| 255 | return kebabName; |
| 256 | }; |
| 257 | |
| 258 | const traverse = (node?: Protocol | null, parentPath?: string, level = 0): void => { |
| 259 | if (!node || !node.data) { |
| 260 | return; |
| 261 | } |
| 262 | |
| 263 | // 1. Normalize componentName (from top-level to data field) |
| 264 | const extendedNode = node as ExtendedFrameStructNode; |
| 265 | const topLevelComponentName = extendedNode.componentName; |
| 266 | if (topLevelComponentName && !node.data.componentName) { |
| 267 | node.data.componentName = topLevelComponentName; |
| 268 | delete extendedNode.componentName; |
| 269 | } |
| 270 | |
| 271 | // 2. Populate elements data from elementIds |
| 272 | if (frames) { |
| 273 | const nodeData = node.data as FrameData & { elementIds?: string[] }; |
| 274 | const elementIds = nodeData.elementIds; |
| 275 | if (elementIds && Array.isArray(elementIds)) { |
| 276 | if (elementIds.length > 0) { |
| 277 | node.data.elements = extractHierarchicalNodesByIds(frames, elementIds, { includeSubtree: true }); |
| 278 | } else { |
| 279 | node.data.elements = []; |
| 280 | } |
| 281 | delete nodeData.elementIds; |
| 282 | } else { |
| 283 | node.data.elements = node.data.elements || []; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | // 3. Annotate with file system paths |
| 288 | const segment = toKebabName(node); |
| 289 | let currentPath: string; |
| 290 | |
| 291 | if (level === 0) { |
| 292 | // Root node always uses base path |
| 293 | currentPath = rootPath; |
no test coverage detected