forked from lumonald-sf/sourceflow-component-extractor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode.js
More file actions
115 lines (100 loc) · 3.53 KB
/
Copy pathcode.js
File metadata and controls
115 lines (100 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
let fileKey = null;
let scannedData = null;
figma.showUI(__html__, { width: 400, height: 440 });
figma.ui.onmessage = async (msg) => {
if (msg.type === 'file-url') {
try {
const match = msg.url.match(/figma\.com\/(?:file|design)\/([\w\d]+)/);
if (!match) throw new Error('Invalid URL');
fileKey = match[1];
scannedData = scanComponents();
figma.ui.resize(400, scannedData.flaggedNames.length > 0 ? 460 : 320);
figma.ui.postMessage({
type: 'scan-result',
uniqueCount: scannedData.uniqueCount,
flaggedNames: scannedData.flaggedNames,
frameCount: scannedData.frameCount
});
} catch (err) {
figma.ui.postMessage({ type: 'error', message: 'Invalid Figma file URL.' });
}
}
if (msg.type === 'confirm-download') {
if (!scannedData) return;
try {
await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
for (const { node, componentText } of scannedData.frames) {
const textNode = figma.createText();
textNode.characters = componentText;
textNode.x = node.x + node.width + 50;
textNode.y = node.y;
figma.currentPage.appendChild(textNode);
}
} catch (err) {
console.error('Font load error:', err.message);
}
const csvLines = [
['Component name', 'Link', 'Figma Page', 'Page Design'],
...scannedData.csvRows
];
const csvContent = csvLines
.map(row => row.map(cell => `"${cell.replace(/"/g, '""')}"`).join(','))
.join('\n');
figma.ui.postMessage({
type: 'download-csv',
fileName: buildFileName(figma.root.name),
data: csvContent
});
}
if (msg.type === 'open-url') {
figma.openExternal(msg.url);
}
if (msg.type === 'close') {
figma.closePlugin('✅ CSV downloaded and text layers added.');
}
};
function shouldSkip(name) {
const lower = name.toLowerCase();
return (
name === '🖼️ Cover' ||
lower.includes('skip component extract') ||
lower.includes('cookies') ||
lower.includes('mobile')
);
}
function scanComponents() {
const allFrames = figma.currentPage.children.filter(n => n.type === 'FRAME');
const filteredFrames = allFrames.filter(parent => !shouldSkip(parent.name));
const csvRows = [];
const frames = [];
const seenNames = new Set();
const flaggedMap = new Map();
for (const parent of filteredFrames) {
const childFrames = parent.children.filter(n =>
['FRAME', 'COMPONENT', 'COMPONENT_SET', 'INSTANCE'].includes(n.type)
);
if (!childFrames.length) continue;
frames.push({ node: parent, componentText: childFrames.map(c => c.name).join('\n') });
for (const child of childFrames) {
seenNames.add(child.name);
const link = `https://www.figma.com/file/${fileKey}?node-id=${encodeURIComponent(child.id)}`;
if (child.name.startsWith('Frame') && !shouldSkip(child.name) && !flaggedMap.has(child.name)) flaggedMap.set(child.name, link);
csvRows.push([child.name, link, figma.currentPage.name, parent.name]);
}
}
return {
frames,
csvRows,
uniqueCount: seenNames.size,
flaggedNames: [...flaggedMap.entries()]
.map(([name, link]) => ({ name, link }))
.sort((a, b) => a.name.localeCompare(b.name)),
frameCount: filteredFrames.length
};
}
function buildFileName(rootName) {
// Greedy .* ensures we match the LAST YYYY-MM in the name and take everything after it
const match = rootName.match(/^.*\d{4}-\d{2} (.+)$/);
const suffix = match ? match[1] : rootName;
return `project-components-${suffix}.csv`;
}