Skip to content

Commit 2bd6f5d

Browse files
committed
adds more types and guards against invalid templates
1 parent dfa17f6 commit 2bd6f5d

File tree

8 files changed

+375
-101
lines changed

8 files changed

+375
-101
lines changed

code.js

+123-40
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,75 @@
33
// src/pluginData.ts
44
var PLUGIN_DATA_NAMESPACE = "codesnippets";
55
var PLUGIN_DATA_KEY = "snippets";
6-
function getPluginData(node) {
7-
return node.getSharedPluginData(PLUGIN_DATA_NAMESPACE, PLUGIN_DATA_KEY);
6+
var CODEGEN_LANGUAGES = [
7+
"BASH",
8+
"CPP",
9+
"CSS",
10+
"GO",
11+
"GRAPHQL",
12+
"HTML",
13+
"JAVASCRIPT",
14+
"JSON",
15+
"KOTLIN",
16+
"PLAINTEXT",
17+
"PYTHON",
18+
"RUBY",
19+
"RUST",
20+
"SQL",
21+
"SWIFT",
22+
"TYPESCRIPT"
23+
];
24+
function getCodegenResultsFromPluginData(node) {
25+
const pluginData = node.getSharedPluginData(
26+
PLUGIN_DATA_NAMESPACE,
27+
PLUGIN_DATA_KEY
28+
);
29+
return pluginDataStringAsValidCodegenResults(pluginData) || [];
30+
}
31+
function setCodegenResultsInPluginData(node, codegenResultArray) {
32+
if (node && arrayContainsCodegenResults(codegenResultArray))
33+
return node.setSharedPluginData(
34+
PLUGIN_DATA_NAMESPACE,
35+
PLUGIN_DATA_KEY,
36+
JSON.stringify(codegenResultArray)
37+
);
838
}
9-
function setPluginData(node, data) {
10-
return node.setSharedPluginData(PLUGIN_DATA_NAMESPACE, PLUGIN_DATA_KEY, data);
39+
function valueIsCodegenLanguage(value) {
40+
return CODEGEN_LANGUAGES.includes(value);
41+
}
42+
function objectIsCodegenResult(object) {
43+
if (typeof object !== "object")
44+
return false;
45+
if (Object.keys(object).length !== 3)
46+
return false;
47+
if (!("title" in object && "code" in object && "language" in object))
48+
return false;
49+
if (typeof object.title !== "string" || typeof object.code !== "string")
50+
return false;
51+
return valueIsCodegenLanguage(object.language);
52+
}
53+
function arrayContainsCodegenResults(array) {
54+
let valid = true;
55+
if (Array.isArray(array)) {
56+
array.forEach((object) => {
57+
if (!objectIsCodegenResult(object)) {
58+
valid = false;
59+
}
60+
});
61+
} else {
62+
valid = false;
63+
}
64+
return valid;
65+
}
66+
function pluginDataStringAsValidCodegenResults(pluginDataString) {
67+
if (!pluginDataString)
68+
return null;
69+
try {
70+
const parsed = JSON.parse(pluginDataString);
71+
return arrayContainsCodegenResults(parsed) ? parsed : null;
72+
} catch (e) {
73+
return null;
74+
}
1175
}
1276

1377
// src/snippets.ts
@@ -21,28 +85,46 @@
2185
regexConditionalOr,
2286
regexConditionalAnd
2387
].join("|");
24-
var regexConditional = new RegExp(`{{([?!])(${regexConditionals})}}`, "g");
25-
async function nodeSnippetTemplateDataArrayFromNode(node, codeSnippetParamsMap) {
88+
var regexConditional = new RegExp(
89+
`{{([?!])(${regexConditionals})}}`,
90+
"g"
91+
);
92+
async function nodeSnippetTemplateDataArrayFromNode(node, codeSnippetParamsMap, globalTemplates) {
2693
const nodeSnippetTemplateDataArray = [];
2794
const seenSnippetTemplates = {};
2895
async function processSnippetTemplatesForNode(node2) {
29-
const pluginData = getPluginData(node2);
30-
if (pluginData && !seenSnippetTemplates[pluginData]) {
31-
seenSnippetTemplates[pluginData] = 1;
32-
const nodeSnippetTemplateData = await hydrateSnippets(
33-
pluginData,
34-
codeSnippetParamsMap,
35-
node2.type
36-
);
37-
nodeSnippetTemplateDataArray.push(nodeSnippetTemplateData);
96+
const codegenResults = getCodegenResultsFromPluginData(node2);
97+
const codegenResultTemplates = [];
98+
if (codegenResults.length) {
99+
const seenKey = JSON.stringify(codegenResults);
100+
if (!seenSnippetTemplates[seenKey]) {
101+
seenSnippetTemplates[seenKey] = 1;
102+
codegenResultTemplates.push(...codegenResults);
103+
}
104+
}
105+
if (globalTemplates) {
106+
const componentTemplates = "key" in node2 ? globalTemplates.components[node2.key] || [] : [];
107+
const typeTemplates = globalTemplates.types[node2.type] || [];
108+
codegenResultTemplates.push(...componentTemplates);
109+
codegenResultTemplates.push(...typeTemplates);
38110
}
111+
const nodeSnippetTemplateData = await hydrateSnippets(
112+
codegenResultTemplates,
113+
codeSnippetParamsMap,
114+
node2.type
115+
);
116+
nodeSnippetTemplateDataArray.push(nodeSnippetTemplateData);
39117
}
40118
await processSnippetTemplatesForNode(node);
41119
if (node.type === "INSTANCE") {
42120
if (node.mainComponent) {
121+
console.log("0", nodeSnippetTemplateDataArray);
43122
await processSnippetTemplatesForNode(node.mainComponent);
123+
console.log("1", nodeSnippetTemplateDataArray);
44124
if (node.mainComponent.parent && node.mainComponent.parent.type === "COMPONENT_SET") {
125+
console.log("2", nodeSnippetTemplateDataArray);
45126
await processSnippetTemplatesForNode(node.mainComponent.parent);
127+
console.log("3", nodeSnippetTemplateDataArray);
46128
}
47129
}
48130
} else if (node.type === "COMPONENT" && node.parent && node.parent.type === "COMPONENT_SET") {
@@ -69,13 +151,12 @@
69151
}
70152
return splitString.join(" ");
71153
}
72-
async function hydrateSnippets(pluginData, codeSnippetParamsMap, nodeType) {
154+
async function hydrateSnippets(codegenResultTemplatesArray, codeSnippetParamsMap, nodeType) {
73155
const { paramsRaw, params } = codeSnippetParamsMap;
74-
const pluginDataArray = JSON.parse(pluginData);
75156
const codegenResultArray = [];
76157
const codegenResultRawTemplatesArray = [];
77-
pluginDataArray.forEach((pluginData2) => {
78-
const lines = pluginData2.code.split("\n");
158+
codegenResultTemplatesArray.forEach((codegenResult) => {
159+
const lines = codegenResult.code.split("\n");
79160
const code = [];
80161
lines.forEach((line) => {
81162
const [matches, qualifies] = lineConditionalMatch(line, params);
@@ -113,14 +194,14 @@
113194
});
114195
const codeString = code.join("\n").replace(/\\\\\n/g, "").replace(/\\\n\\/g, "").replace(/\\\n/g, " ");
115196
codegenResultArray.push({
116-
title: pluginData2.title,
117-
language: pluginData2.language,
197+
title: codegenResult.title,
198+
language: codegenResult.language,
118199
code: codeString
119200
});
120201
codegenResultRawTemplatesArray.push({
121-
title: `${pluginData2.title}: Template (${nodeType})`,
202+
title: `${codegenResult.title}: Template (${nodeType})`,
122203
language: "PLAINTEXT",
123-
code: pluginData2.code
204+
code: codegenResult.code
124205
});
125206
});
126207
return {
@@ -399,16 +480,14 @@
399480
performGetComponentData,
400481
performGetNodeData
401482
};
402-
function performImport(eventData) {
483+
function performImport(data) {
403484
const componentsByKey = getComponentsInFileByKey();
404-
const data = JSON.parse(eventData);
405485
let componentCount = 0;
406486
for (let componentKey in data) {
407-
const dataToSave = JSON.stringify(data[componentKey]);
408487
const component = componentsByKey[componentKey];
409488
if (component) {
410489
componentCount++;
411-
setPluginData(component, dataToSave);
490+
setCodegenResultsInPluginData(component, data[componentKey]);
412491
}
413492
}
414493
const s = componentCount === 1 ? "" : "s";
@@ -418,15 +497,16 @@
418497
const data = {};
419498
const components = findComponentNodesInFile();
420499
components.forEach((component) => {
421-
const pluginData = getPluginData(component);
422-
if (pluginData) {
423-
data[component.key] = JSON.parse(pluginData);
500+
const codegenResults = getCodegenResultsFromPluginData(component);
501+
if (codegenResults) {
502+
data[component.key] = codegenResults;
424503
}
425504
});
426-
figma.ui.postMessage({
505+
const message = {
427506
type: "EXPORT",
428507
code: JSON.stringify(data, null, 2)
429-
});
508+
};
509+
figma.ui.postMessage(message);
430510
}
431511
function performGetComponentData() {
432512
const components = findComponentNodesInFile();
@@ -450,10 +530,11 @@
450530
}
451531
return into;
452532
}, componentData);
453-
figma.ui.postMessage({
533+
const message = {
454534
type: "COMPONENT_DATA",
455535
code: JSON.stringify(data, null, 2)
456-
});
536+
};
537+
figma.ui.postMessage(message);
457538
}
458539
async function performGetNodeData() {
459540
const nodes = figma.currentPage.selection;
@@ -464,10 +545,11 @@
464545
return;
465546
})
466547
);
467-
figma.ui.postMessage({
548+
const message = {
468549
type: "NODE_DATA",
469550
code: JSON.stringify(data, null, 2)
470-
});
551+
};
552+
figma.ui.postMessage(message);
471553
}
472554
function keyFromNode(node) {
473555
return `${node.name} ${node.type} ${node.id}`;
@@ -503,7 +585,7 @@
503585
if (event.type === "INITIALIZE") {
504586
handleCurrentSelection();
505587
} else if (event.type === "SAVE") {
506-
setPluginData(figma.currentPage.selection[0], event.data);
588+
setCodegenResultsInPluginData(figma.currentPage.selection[0], event.data);
507589
} else {
508590
console.log("UNKNOWN EVENT", event);
509591
}
@@ -602,15 +684,16 @@
602684
function handleCurrentSelection() {
603685
const node = figma.currentPage.selection[0];
604686
try {
605-
const nodePluginData = node ? getPluginData(node) : null;
687+
const nodePluginData = node ? getCodegenResultsFromPluginData(node) : null;
606688
const nodeId = node ? node.id : null;
607689
const nodeType = node ? node.type : null;
608-
figma.ui.postMessage({
690+
const message = {
609691
type: "SELECTION",
610692
nodeId,
611693
nodeType,
612694
nodePluginData
613-
});
695+
};
696+
figma.ui.postMessage(message);
614697
return node;
615698
} catch (e) {
616699
return node;

src/bulk.ts

+19-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { getPluginData, setPluginData } from "./pluginData";
1+
import {
2+
getCodegenResultsFromPluginData,
3+
setCodegenResultsInPluginData,
4+
} from "./pluginData";
25
import { paramsFromNode } from "./params";
36

47
/**
@@ -15,19 +18,17 @@ export const bulk = {
1518
/**
1619
* Import code snippet templates into components in bulk via JSON.
1720
* https://github.com/figma/code-snippet-editor-plugin#importexport
18-
* @param eventData stringified CodegenResultTemplatesByComponentKey
21+
* @param data CodegenResultTemplatesByComponentKey
1922
* @returns void
2023
*/
21-
function performImport(eventData: string) {
24+
function performImport(data: CodegenResultTemplatesByComponentKey) {
2225
const componentsByKey = getComponentsInFileByKey();
23-
const data: CodegenResultTemplatesByComponentKey = JSON.parse(eventData);
2426
let componentCount = 0;
2527
for (let componentKey in data) {
26-
const dataToSave = JSON.stringify(data[componentKey]);
2728
const component = componentsByKey[componentKey];
2829
if (component) {
2930
componentCount++;
30-
setPluginData(component, dataToSave);
31+
setCodegenResultsInPluginData(component, data[componentKey]);
3132
}
3233
}
3334
const s = componentCount === 1 ? "" : "s";
@@ -43,15 +44,16 @@ function performExport() {
4344
const data: CodegenResultTemplatesByComponentKey = {};
4445
const components = findComponentNodesInFile();
4546
components.forEach((component) => {
46-
const pluginData = getPluginData(component);
47-
if (pluginData) {
48-
data[component.key] = JSON.parse(pluginData) as CodegenResult[];
47+
const codegenResults = getCodegenResultsFromPluginData(component);
48+
if (codegenResults) {
49+
data[component.key] = codegenResults;
4950
}
5051
});
51-
figma.ui.postMessage({
52+
const message: EventToBulk = {
5253
type: "EXPORT",
5354
code: JSON.stringify(data, null, 2),
54-
});
55+
};
56+
figma.ui.postMessage(message);
5557
}
5658

5759
/**
@@ -81,10 +83,11 @@ function performGetComponentData() {
8183
}
8284
return into;
8385
}, componentData);
84-
figma.ui.postMessage({
86+
const message: EventToBulk = {
8587
type: "COMPONENT_DATA",
8688
code: JSON.stringify(data, null, 2),
87-
});
89+
};
90+
figma.ui.postMessage(message);
8891
}
8992

9093
/**
@@ -101,10 +104,11 @@ async function performGetNodeData() {
101104
return;
102105
})
103106
);
104-
figma.ui.postMessage({
107+
const message: EventToBulk = {
105108
type: "NODE_DATA",
106109
code: JSON.stringify(data, null, 2),
107-
});
110+
};
111+
figma.ui.postMessage(message);
108112
}
109113

110114
/**

0 commit comments

Comments
 (0)