analyze-figma-json.mjs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. // 分析蓝湖 FigmaJSON:画板尺寸、切图(导出标记)、文本字体、图片引用、色板
  2. import { readFileSync } from 'node:fs'
  3. const file = process.argv[2]
  4. const j = JSON.parse(readFileSync(file, 'utf8'))
  5. const ab = j.artboard
  6. const fonts = new Map() // name|postScript|size|weight -> {count, samples:Set}
  7. const slices = []
  8. const images = []
  9. const colors = new Map()
  10. function hex(c) {
  11. if (!c) return null
  12. const h = (v) => Math.round(v * 255).toString(16).padStart(2, '0')
  13. return `${h(c.r)}${h(c.g)}${h(c.b)}${c.a !== undefined && c.a < 1 ? h(c.a) : ''}`
  14. }
  15. function addFont(f, text) {
  16. if (!f || !f.name) return
  17. const key = `${f.name} | ${f.postScriptName || ''} | ${f.size}px | w${f.fontWeight ?? ''}${f.bold ? ' bold' : ''}`
  18. const cur = fonts.get(key) || { count: 0, samples: new Set() }
  19. cur.count += 1
  20. if (text) cur.samples.add(String(text).slice(0, 12))
  21. fonts.set(key, cur)
  22. }
  23. function walk(node, path) {
  24. const p = path ? `${path}/${node.name}` : node.name
  25. if (node.type === 'textLayer' && node.text) {
  26. addFont(node.text.style?.font, node.text.style?.content)
  27. for (const r of node.text.runs || node.text.styleRuns || []) addFont(r.font || r.style?.font, r.content || r.text)
  28. }
  29. if (node.hasExportImage || node.hasExportDDSImage) {
  30. slices.push({ path: p, type: node.type, w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0), dds: !!node.hasExportDDSImage })
  31. }
  32. for (const f of node.style?.fills || []) {
  33. if (f.type === 'color' && f.color) {
  34. const c = hex(f.color)
  35. if (c) colors.set(c, (colors.get(c) || 0) + 1)
  36. }
  37. const url = f.ddsImage?.imageUrl || f.image?.imageUrl || f.imageUrl
  38. if (f.type === 'image' && url) {
  39. images.push({ path: p, url, w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0) })
  40. }
  41. }
  42. for (const ch of node.layers || []) walk(ch, p)
  43. }
  44. for (const l of ab.layers || []) walk(l, '')
  45. const out = {
  46. artboard: ab.name,
  47. frame: { w: ab.frame.width, h: ab.frame.height },
  48. sliceCount: slices.length,
  49. sliceLayers: slices,
  50. fonts: [...fonts.entries()].sort((a, b) => b[1].count - a[1].count).map(([k, v]) => ({ font: k, count: v.count, samples: [...v.samples].slice(0, 4) })),
  51. imageRefCount: images.length,
  52. imageRefs: images,
  53. topColors: [...colors.entries()].sort((a, b) => b[1] - a[1]).slice(0, 24).map(([k, v]) => ({ color: `#${k}`, count: v })),
  54. assetFiles: j.assets || [],
  55. }
  56. console.log(JSON.stringify(out, null, 1))