@figma/code-connect 1.4.3 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +2 -22
  2. package/dist/commands/connect.d.ts.map +1 -1
  3. package/dist/commands/connect.js +61 -10
  4. package/dist/commands/connect.js.map +1 -1
  5. package/dist/commands/connect_template.d.ts +36 -0
  6. package/dist/commands/connect_template.d.ts.map +1 -0
  7. package/dist/commands/connect_template.js +324 -0
  8. package/dist/commands/connect_template.js.map +1 -0
  9. package/dist/commands/preview_utils.d.ts +49 -0
  10. package/dist/commands/preview_utils.d.ts.map +1 -0
  11. package/dist/commands/preview_utils.js +390 -0
  12. package/dist/commands/preview_utils.js.map +1 -0
  13. package/dist/connect/batch_templates.d.ts +11 -0
  14. package/dist/connect/batch_templates.d.ts.map +1 -0
  15. package/dist/connect/batch_templates.js +70 -0
  16. package/dist/connect/batch_templates.js.map +1 -0
  17. package/dist/connect/create.d.ts.map +1 -1
  18. package/dist/connect/create.js +13 -0
  19. package/dist/connect/create.js.map +1 -1
  20. package/dist/connect/create_template.d.ts +22 -0
  21. package/dist/connect/create_template.d.ts.map +1 -0
  22. package/dist/connect/create_template.js +195 -0
  23. package/dist/connect/create_template.js.map +1 -0
  24. package/dist/connect/delete_docs.d.ts +2 -1
  25. package/dist/connect/delete_docs.d.ts.map +1 -1
  26. package/dist/connect/delete_docs.js +2 -2
  27. package/dist/connect/delete_docs.js.map +1 -1
  28. package/dist/connect/figma_rest_api.d.ts +1 -5
  29. package/dist/connect/figma_rest_api.d.ts.map +1 -1
  30. package/dist/connect/figma_rest_api.js +9 -3
  31. package/dist/connect/figma_rest_api.js.map +1 -1
  32. package/dist/connect/project.d.ts +9 -7
  33. package/dist/connect/project.d.ts.map +1 -1
  34. package/dist/connect/project.js +39 -25
  35. package/dist/connect/project.js.map +1 -1
  36. package/dist/connect/raw_templates.d.ts +8 -1
  37. package/dist/connect/raw_templates.d.ts.map +1 -1
  38. package/dist/connect/raw_templates.js +20 -7
  39. package/dist/connect/raw_templates.js.map +1 -1
  40. package/dist/connect/upload.d.ts +3 -1
  41. package/dist/connect/upload.d.ts.map +1 -1
  42. package/dist/connect/upload.js +23 -8
  43. package/dist/connect/upload.js.map +1 -1
  44. package/dist/connect/validation.d.ts +2 -2
  45. package/dist/connect/validation.d.ts.map +1 -1
  46. package/dist/connect/validation.js +4 -4
  47. package/dist/connect/validation.js.map +1 -1
  48. package/dist/connect/wizard/helpers.js.map +1 -1
  49. package/dist/storybook/convert.d.ts.map +1 -1
  50. package/dist/storybook/convert.js +1 -1
  51. package/dist/storybook/convert.js.map +1 -1
  52. package/figma-types.d.ts +3 -0
  53. package/package.json +6 -2
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.collectNodesToPreview = collectNodesToPreview;
40
+ exports.filterTemplatesForNodes = filterTemplatesForNodes;
41
+ exports.getPrettierParserMap = getPrettierParserMap;
42
+ exports.isPrettierParseable = isPrettierParseable;
43
+ exports.formatSnippet = formatSnippet;
44
+ exports.displayResults = displayResults;
45
+ exports.handlePreview = handlePreview;
46
+ const fs_1 = __importDefault(require("fs"));
47
+ const path_1 = __importDefault(require("path"));
48
+ const prettier = __importStar(require("prettier"));
49
+ const connect_1 = require("./connect");
50
+ const logging_1 = require("../common/logging");
51
+ const validation_1 = require("../connect/validation");
52
+ const project_1 = require("../connect/project");
53
+ const figma_rest_api_1 = require("../connect/figma_rest_api");
54
+ const fetch_1 = require("../common/fetch");
55
+ /**
56
+ * Collect nodes to preview from file arguments.
57
+ * Matches by exact path first, then by basename.
58
+ */
59
+ async function collectNodesToPreview(files, allCodeConnectObjects, dir, cmd) {
60
+ const nodesToPreview = [];
61
+ for (const filePath of files) {
62
+ const resolvedPath = path_1.default.isAbsolute(filePath) ? filePath : path_1.default.resolve(dir, filePath);
63
+ // Try exact path first
64
+ if (fs_1.default.existsSync(resolvedPath) && fs_1.default.statSync(resolvedPath).isFile()) {
65
+ const docs = allCodeConnectObjects.filter((d) => path_1.default.resolve(d._codeConnectFilePath || '') === resolvedPath);
66
+ if (docs.length > 0) {
67
+ if (docs.length === 1) {
68
+ logging_1.logger.info(`Found: ${filePath}`);
69
+ }
70
+ else {
71
+ logging_1.logger.info(`Found ${docs.length} component definition(s) in ${filePath}`);
72
+ }
73
+ for (const doc of docs) {
74
+ const parsed = (0, validation_1.parseFigmaNode)(cmd.verbose, doc, true);
75
+ if (parsed) {
76
+ nodesToPreview.push({
77
+ fileKey: parsed.fileKey,
78
+ nodeId: parsed.nodeId,
79
+ url: doc.figmaNode,
80
+ filePath: path_1.default.relative(dir, doc._codeConnectFilePath || ''),
81
+ });
82
+ }
83
+ else {
84
+ logging_1.logger.error(`Failed to parse figmaNode from file: ${filePath}`);
85
+ }
86
+ }
87
+ }
88
+ else {
89
+ logging_1.logger.error(`Not a valid Code Connect file: ${filePath}`);
90
+ }
91
+ continue;
92
+ }
93
+ // Fall back to basename matching
94
+ const fileName = path_1.default.basename(filePath);
95
+ if (cmd.verbose) {
96
+ logging_1.logger.debug(`Searching for files matching: ${fileName}`);
97
+ logging_1.logger.debug(`Total Code Connect objects: ${allCodeConnectObjects.length}`);
98
+ }
99
+ const matches = allCodeConnectObjects.filter((d) => path_1.default.basename(d._codeConnectFilePath || '') === fileName);
100
+ if (matches.length === 0) {
101
+ logging_1.logger.error(`No files found matching: ${filePath}`);
102
+ continue;
103
+ }
104
+ if (matches.length === 1) {
105
+ logging_1.logger.info(`Found: ${matches[0]._codeConnectFilePath}`);
106
+ }
107
+ else {
108
+ const uniqueFiles = new Set(matches.map((d) => d._codeConnectFilePath));
109
+ if (uniqueFiles.size === 1) {
110
+ logging_1.logger.info(`Found ${matches.length} component definition(s) in ${path_1.default.relative(dir, Array.from(uniqueFiles)[0] || '')}`);
111
+ }
112
+ else {
113
+ logging_1.logger.info(`Found ${matches.length} component definition(s) in ${uniqueFiles.size} files:`);
114
+ for (const fp of uniqueFiles) {
115
+ const count = matches.filter((d) => d._codeConnectFilePath === fp).length;
116
+ logging_1.logger.info(` - ${path_1.default.relative(dir, fp || '')} (${count} definition${count > 1 ? 's' : ''})`);
117
+ }
118
+ }
119
+ }
120
+ for (const doc of matches) {
121
+ const parsed = (0, validation_1.parseFigmaNode)(cmd.verbose, doc, true);
122
+ if (parsed) {
123
+ nodesToPreview.push({
124
+ fileKey: parsed.fileKey,
125
+ nodeId: parsed.nodeId,
126
+ url: doc.figmaNode,
127
+ filePath: path_1.default.relative(dir, doc._codeConnectFilePath || ''),
128
+ });
129
+ }
130
+ }
131
+ }
132
+ return nodesToPreview;
133
+ }
134
+ /**
135
+ * Filter templates to only those matching the requested node IDs.
136
+ */
137
+ function filterTemplatesForNodes(nodeIds, allTemplates) {
138
+ return allTemplates.filter((template) => {
139
+ if (!template.figmaNode)
140
+ return false;
141
+ const nodeIdMatch = template.figmaNode.match(/node-id=([^&\s]+)/);
142
+ if (!nodeIdMatch?.[1])
143
+ return false;
144
+ const templateNodeId = nodeIdMatch[1].replace(/-/g, ':');
145
+ return nodeIds.includes(templateNodeId);
146
+ });
147
+ }
148
+ /**
149
+ * Aliases for Code Connect labels that don't directly match a Prettier language name.
150
+ */
151
+ const CC_LABEL_ALIASES = {
152
+ react: 'typescript',
153
+ code: 'typescript',
154
+ };
155
+ /**
156
+ * Map from Code Connect language labels to Prettier parser names,
157
+ * built lazily from Prettier's getSupportInfo() on first access.
158
+ * Lazy to avoid triggering plugin loading at module import time.
159
+ */
160
+ let _prettierParserMap = null;
161
+ function getPrettierParserMap() {
162
+ if (_prettierParserMap)
163
+ return _prettierParserMap;
164
+ const info = prettier.getSupportInfo();
165
+ _prettierParserMap = {};
166
+ for (const lang of info.languages) {
167
+ if (lang.parsers?.[0]) {
168
+ _prettierParserMap[lang.name.toLowerCase()] = lang.parsers[0];
169
+ }
170
+ }
171
+ for (const [alias, target] of Object.entries(CC_LABEL_ALIASES)) {
172
+ if (_prettierParserMap[target]) {
173
+ _prettierParserMap[alias] = _prettierParserMap[target];
174
+ }
175
+ }
176
+ return _prettierParserMap;
177
+ }
178
+ /**
179
+ * Check if a snippet can be parsed by Prettier.
180
+ * Returns true for languages without a Prettier parser (nothing to validate).
181
+ */
182
+ async function isPrettierParseable(snippet, language) {
183
+ const lang = language?.toLowerCase();
184
+ const parser = lang ? getPrettierParserMap()[lang] : 'typescript';
185
+ if (!parser) {
186
+ return true;
187
+ }
188
+ try {
189
+ await prettier.format(snippet, { parser });
190
+ return true;
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ }
196
+ /**
197
+ * Format snippet with Prettier.
198
+ * Returns the snippet unmodified for languages Prettier doesn't support.
199
+ */
200
+ async function formatSnippet(snippet, language) {
201
+ const lang = language?.toLowerCase();
202
+ const parser = lang ? getPrettierParserMap()[lang] : 'typescript';
203
+ if (!parser) {
204
+ return snippet;
205
+ }
206
+ try {
207
+ let formatted = await prettier.format(snippet, {
208
+ parser,
209
+ semi: false,
210
+ singleQuote: true,
211
+ printWidth: 80,
212
+ tabWidth: 2,
213
+ });
214
+ formatted = formatted.replace(/^;\s*/, '');
215
+ return formatted;
216
+ }
217
+ catch {
218
+ logging_1.logger.info(`Autoformatting couldn't be applied: language not supported or code is malformed`);
219
+ return snippet;
220
+ }
221
+ }
222
+ /**
223
+ * Display results with terminal colors
224
+ */
225
+ function displayResults(results) {
226
+ console.log('');
227
+ const successCount = results.filter((r) => r.success).length;
228
+ const errorCount = results.filter((r) => !r.success).length;
229
+ const purple = '\x1b[38;5;93m';
230
+ const red = '\x1b[38;5;196m';
231
+ const gray = '\x1b[38;5;243m';
232
+ const reset = '\x1b[0m';
233
+ const bold = '\x1b[1m';
234
+ for (const result of results) {
235
+ if (result.success && result.snippet) {
236
+ const componentInfo = result.component ? ` ${gray}→ ${result.component}${reset}` : '';
237
+ console.log(`${purple}●${reset} ${bold}${result.filePath}${reset}${componentInfo}`);
238
+ console.log(` ${gray}${result.url}${reset}`);
239
+ console.log('');
240
+ const indentedSnippet = result.snippet
241
+ .trim()
242
+ .split('\n')
243
+ .map((line) => ' ' + line)
244
+ .join('\n');
245
+ console.log(indentedSnippet);
246
+ console.log('');
247
+ }
248
+ else {
249
+ const componentInfo = result.component ? ` ${gray}(${result.component})${reset}` : '';
250
+ console.log(`${red}✕${reset} ${bold}${result.filePath}${reset}${componentInfo}`);
251
+ console.log(` ${gray}${result.url}${reset}`);
252
+ console.log(` ${red}Error:${reset} ${result.error}`);
253
+ console.log('');
254
+ }
255
+ }
256
+ console.log(`${bold}Summary:${reset} ${purple}${successCount} succeeded${reset}, ${errorCount > 0 ? `${red}${errorCount} failed${reset}` : `${gray}${errorCount} failed${reset}`}`);
257
+ }
258
+ /**
259
+ * Handle the preview command
260
+ */
261
+ async function handlePreview(files, cmd) {
262
+ (0, connect_1.setupHandler)(cmd);
263
+ const dir = cmd.dir ?? process.cwd();
264
+ const projectInfo = await (0, project_1.getProjectInfo)(dir, cmd.config);
265
+ const accessToken = (0, connect_1.getAccessTokenOrExit)(cmd);
266
+ const outputFormat = cmd.output || 'table';
267
+ const allCodeConnectObjects = await (0, connect_1.getCodeConnectObjects)(cmd, projectInfo, true);
268
+ let nodesToCheck;
269
+ if (files && files.length > 0) {
270
+ nodesToCheck = await collectNodesToPreview(files, allCodeConnectObjects, dir, cmd);
271
+ }
272
+ else {
273
+ logging_1.logger.info('Previewing all local Code Connect files...');
274
+ nodesToCheck = [];
275
+ for (const doc of allCodeConnectObjects) {
276
+ const parsed = (0, validation_1.parseFigmaNode)(cmd.verbose, doc, true);
277
+ if (parsed) {
278
+ nodesToCheck.push({
279
+ fileKey: parsed.fileKey,
280
+ nodeId: parsed.nodeId,
281
+ url: doc.figmaNode,
282
+ filePath: path_1.default.relative(dir, doc._codeConnectFilePath || ''),
283
+ });
284
+ }
285
+ }
286
+ }
287
+ if (nodesToCheck.length === 0) {
288
+ (0, logging_1.exitWithError)('No valid Code Connect files found to preview');
289
+ }
290
+ logging_1.logger.info(`Previewing ${nodesToCheck.length} component(s)...`);
291
+ // Group nodes by fileKey for batching
292
+ const nodesByFileKey = {};
293
+ for (const node of nodesToCheck) {
294
+ if (!nodesByFileKey[node.fileKey]) {
295
+ nodesByFileKey[node.fileKey] = [];
296
+ }
297
+ nodesByFileKey[node.fileKey].push(node);
298
+ }
299
+ const results = [];
300
+ for (const [fileKey, nodes] of Object.entries(nodesByFileKey)) {
301
+ const baseApiUrl = (0, figma_rest_api_1.getApiUrl)(nodes[0].url, cmd.apiUrl || projectInfo.config.apiUrl);
302
+ const allNodeIds = nodes.map((n) => n.nodeId);
303
+ // Deduplicate — multiple figma.connect() calls may share the same nodeId;
304
+ // the individual templates are sent in figmaDocs and the server iterates per-template.
305
+ const nodeIds = [...new Set(allNodeIds)];
306
+ // Only send templates from the requested files — other files' templates
307
+ // for the same nodeId exist on the server and shouldn't be rendered individually.
308
+ const requestedFilePaths = new Set(nodes.map((n) => path_1.default.resolve(dir, n.filePath)));
309
+ const requiredTemplates = filterTemplatesForNodes(allNodeIds, allCodeConnectObjects).filter((t) => requestedFilePaths.has(path_1.default.resolve(t._codeConnectFilePath || '')));
310
+ const figmaDocs = { all: requiredTemplates };
311
+ try {
312
+ const response = await fetch_1.request.post(`${baseApiUrl}/code_connect/preview_snippets?file_key=${fileKey}`, { nodeIds, figmaDocs }, { headers: (0, figma_rest_api_1.getHeaders)(accessToken) });
313
+ if (response.response.status === 200 && response.data.meta?.results) {
314
+ // Track match index per nodeId so duplicate node IDs get the correct file attribution.
315
+ // The server returns results in the same order as the templates we sent.
316
+ const nodeMatchIndex = {};
317
+ for (const result of response.data.meta.results) {
318
+ const idx = nodeMatchIndex[result.nodeId] ?? 0;
319
+ const matchingNodes = nodes.filter((n) => n.nodeId === result.nodeId);
320
+ const node = matchingNodes[idx] || matchingNodes[0];
321
+ nodeMatchIndex[result.nodeId] = idx + 1;
322
+ results.push({
323
+ url: node?.url || result.nodeUrl,
324
+ nodeId: result.nodeId,
325
+ filePath: node?.filePath || '',
326
+ success: !result.error && !!result.snippet,
327
+ snippet: result.snippet,
328
+ language: result.language,
329
+ component: result.component,
330
+ error: result.error || (!result.snippet ? 'No snippet returned by server' : undefined),
331
+ });
332
+ }
333
+ }
334
+ else {
335
+ for (const node of nodes) {
336
+ results.push({
337
+ url: node.url,
338
+ nodeId: node.nodeId,
339
+ filePath: node.filePath,
340
+ success: false,
341
+ error: `API request failed with status ${response.response.status}`,
342
+ });
343
+ }
344
+ }
345
+ }
346
+ catch (err) {
347
+ const errorMsg = (0, fetch_1.isFetchError)(err)
348
+ ? err.data?.message || err.response.statusText
349
+ : String(err);
350
+ logging_1.logger.error(`Failed to preview components in file ${fileKey}: ${errorMsg}`);
351
+ for (const node of nodes) {
352
+ results.push({
353
+ url: node.url,
354
+ nodeId: node.nodeId,
355
+ filePath: node.filePath,
356
+ success: false,
357
+ error: errorMsg,
358
+ });
359
+ }
360
+ }
361
+ }
362
+ // Validate snippet syntax
363
+ for (const result of results) {
364
+ if (result.success && result.snippet) {
365
+ const prettierValid = await isPrettierParseable(result.snippet, result.language);
366
+ if (!prettierValid) {
367
+ // Prettier can parse this language but failed — mark as error
368
+ result.success = false;
369
+ result.error = 'Snippet has syntax errors and may not be valid code';
370
+ }
371
+ }
372
+ }
373
+ if (outputFormat === 'json') {
374
+ console.log(JSON.stringify(results, null, 2));
375
+ }
376
+ else {
377
+ const formattedResults = await Promise.all(results.map(async (result) => {
378
+ if (result.success && result.snippet) {
379
+ const formattedSnippet = await formatSnippet(result.snippet, result.language);
380
+ return { ...result, snippet: formattedSnippet };
381
+ }
382
+ return result;
383
+ }));
384
+ displayResults(formattedResults);
385
+ }
386
+ if (results.every((r) => !r.success)) {
387
+ process.exit(1);
388
+ }
389
+ }
390
+ //# sourceMappingURL=preview_utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview_utils.js","sourceRoot":"","sources":["../../src/commands/preview_utils.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport * as prettier from 'prettier'\nimport { BaseCommand, getCodeConnectObjects, getAccessTokenOrExit, setupHandler } from './connect'\nimport { exitWithError, logger } from '../common/logging'\nimport { CodeConnectJSON } from '../connect/figma_connect'\nimport { parseFigmaNode } from '../connect/validation'\nimport { getProjectInfo } from '../connect/project'\nimport { getApiUrl, getHeaders } from '../connect/figma_rest_api'\nimport { request, isFetchError } from '../common/fetch'\n\nexport interface NodeToPreview {\n fileKey: string\n nodeId: string\n url: string\n filePath: string\n}\n\nexport interface PreviewResult {\n url: string\n nodeId: string\n filePath: string\n success: boolean\n snippet?: string\n language?: string\n component?: string\n error?: string\n}\n\n/**\n * Collect nodes to preview from file arguments.\n * Matches by exact path first, then by basename.\n */\nexport async function collectNodesToPreview(\n files: string[],\n allCodeConnectObjects: CodeConnectJSON[],\n dir: string,\n cmd: BaseCommand,\n): Promise<NodeToPreview[]> {\n const nodesToPreview: NodeToPreview[] = []\n\n for (const filePath of files) {\n const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(dir, filePath)\n\n // Try exact path first\n if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) {\n const docs = allCodeConnectObjects.filter(\n (d) => path.resolve(d._codeConnectFilePath || '') === resolvedPath,\n )\n if (docs.length > 0) {\n if (docs.length === 1) {\n logger.info(`Found: ${filePath}`)\n } else {\n logger.info(`Found ${docs.length} component definition(s) in ${filePath}`)\n }\n for (const doc of docs) {\n const parsed = parseFigmaNode(cmd.verbose, doc, true)\n if (parsed) {\n nodesToPreview.push({\n fileKey: parsed.fileKey,\n nodeId: parsed.nodeId,\n url: doc.figmaNode,\n filePath: path.relative(dir, doc._codeConnectFilePath || ''),\n })\n } else {\n logger.error(`Failed to parse figmaNode from file: ${filePath}`)\n }\n }\n } else {\n logger.error(`Not a valid Code Connect file: ${filePath}`)\n }\n continue\n }\n\n // Fall back to basename matching\n const fileName = path.basename(filePath)\n if (cmd.verbose) {\n logger.debug(`Searching for files matching: ${fileName}`)\n logger.debug(`Total Code Connect objects: ${allCodeConnectObjects.length}`)\n }\n\n const matches = allCodeConnectObjects.filter(\n (d) => path.basename(d._codeConnectFilePath || '') === fileName,\n )\n\n if (matches.length === 0) {\n logger.error(`No files found matching: ${filePath}`)\n continue\n }\n\n if (matches.length === 1) {\n logger.info(`Found: ${matches[0]._codeConnectFilePath}`)\n } else {\n const uniqueFiles = new Set(matches.map((d) => d._codeConnectFilePath))\n if (uniqueFiles.size === 1) {\n logger.info(\n `Found ${matches.length} component definition(s) in ${path.relative(dir, Array.from(uniqueFiles)[0] || '')}`,\n )\n } else {\n logger.info(`Found ${matches.length} component definition(s) in ${uniqueFiles.size} files:`)\n for (const fp of uniqueFiles) {\n const count = matches.filter((d) => d._codeConnectFilePath === fp).length\n logger.info(\n ` - ${path.relative(dir, fp || '')} (${count} definition${count > 1 ? 's' : ''})`,\n )\n }\n }\n }\n\n for (const doc of matches) {\n const parsed = parseFigmaNode(cmd.verbose, doc, true)\n if (parsed) {\n nodesToPreview.push({\n fileKey: parsed.fileKey,\n nodeId: parsed.nodeId,\n url: doc.figmaNode,\n filePath: path.relative(dir, doc._codeConnectFilePath || ''),\n })\n }\n }\n }\n\n return nodesToPreview\n}\n\n/**\n * Filter templates to only those matching the requested node IDs.\n */\nexport function filterTemplatesForNodes(\n nodeIds: string[],\n allTemplates: CodeConnectJSON[],\n): CodeConnectJSON[] {\n return allTemplates.filter((template) => {\n if (!template.figmaNode) return false\n const nodeIdMatch = template.figmaNode.match(/node-id=([^&\\s]+)/)\n if (!nodeIdMatch?.[1]) return false\n const templateNodeId = nodeIdMatch[1].replace(/-/g, ':')\n return nodeIds.includes(templateNodeId)\n })\n}\n\n/**\n * Aliases for Code Connect labels that don't directly match a Prettier language name.\n */\nconst CC_LABEL_ALIASES: Record<string, string> = {\n react: 'typescript',\n code: 'typescript',\n}\n\n/**\n * Map from Code Connect language labels to Prettier parser names,\n * built lazily from Prettier's getSupportInfo() on first access.\n * Lazy to avoid triggering plugin loading at module import time.\n */\nlet _prettierParserMap: Record<string, string> | null = null\n\nexport function getPrettierParserMap(): Record<string, string> {\n if (_prettierParserMap) return _prettierParserMap\n\n const info = prettier.getSupportInfo() as {\n languages: Array<{ name: string; parsers?: string[] }>\n }\n _prettierParserMap = {}\n\n for (const lang of info.languages) {\n if (lang.parsers?.[0]) {\n _prettierParserMap[lang.name.toLowerCase()] = lang.parsers[0]\n }\n }\n\n for (const [alias, target] of Object.entries(CC_LABEL_ALIASES)) {\n if (_prettierParserMap[target]) {\n _prettierParserMap[alias] = _prettierParserMap[target]\n }\n }\n\n return _prettierParserMap\n}\n\n/**\n * Check if a snippet can be parsed by Prettier.\n * Returns true for languages without a Prettier parser (nothing to validate).\n */\nexport async function isPrettierParseable(snippet: string, language?: string): Promise<boolean> {\n const lang = language?.toLowerCase()\n const parser = lang ? getPrettierParserMap()[lang] : 'typescript'\n\n if (!parser) {\n return true\n }\n\n try {\n await prettier.format(snippet, { parser })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Format snippet with Prettier.\n * Returns the snippet unmodified for languages Prettier doesn't support.\n */\nexport async function formatSnippet(snippet: string, language?: string): Promise<string> {\n const lang = language?.toLowerCase()\n const parser = lang ? getPrettierParserMap()[lang] : 'typescript'\n\n if (!parser) {\n return snippet\n }\n\n try {\n let formatted = await prettier.format(snippet, {\n parser,\n semi: false,\n singleQuote: true,\n printWidth: 80,\n tabWidth: 2,\n })\n formatted = formatted.replace(/^;\\s*/, '')\n return formatted\n } catch {\n logger.info(`Autoformatting couldn't be applied: language not supported or code is malformed`)\n return snippet\n }\n}\n\n/**\n * Display results with terminal colors\n */\nexport function displayResults(results: PreviewResult[]): void {\n console.log('')\n const successCount = results.filter((r) => r.success).length\n const errorCount = results.filter((r) => !r.success).length\n\n const purple = '\\x1b[38;5;93m'\n const red = '\\x1b[38;5;196m'\n const gray = '\\x1b[38;5;243m'\n const reset = '\\x1b[0m'\n const bold = '\\x1b[1m'\n\n for (const result of results) {\n if (result.success && result.snippet) {\n const componentInfo = result.component ? ` ${gray}→ ${result.component}${reset}` : ''\n console.log(`${purple}●${reset} ${bold}${result.filePath}${reset}${componentInfo}`)\n console.log(` ${gray}${result.url}${reset}`)\n console.log('')\n\n const indentedSnippet = result.snippet\n .trim()\n .split('\\n')\n .map((line) => ' ' + line)\n .join('\\n')\n console.log(indentedSnippet)\n console.log('')\n } else {\n const componentInfo = result.component ? ` ${gray}(${result.component})${reset}` : ''\n console.log(`${red}✕${reset} ${bold}${result.filePath}${reset}${componentInfo}`)\n console.log(` ${gray}${result.url}${reset}`)\n console.log(` ${red}Error:${reset} ${result.error}`)\n console.log('')\n }\n }\n\n console.log(\n `${bold}Summary:${reset} ${purple}${successCount} succeeded${reset}, ${errorCount > 0 ? `${red}${errorCount} failed${reset}` : `${gray}${errorCount} failed${reset}`}`,\n )\n}\n\n/**\n * Handle the preview command\n */\nexport async function handlePreview(files: string[], cmd: BaseCommand & { output?: string }) {\n setupHandler(cmd)\n\n const dir = cmd.dir ?? process.cwd()\n const projectInfo = await getProjectInfo(dir, cmd.config)\n const accessToken = getAccessTokenOrExit(cmd)\n const outputFormat = cmd.output || 'table'\n\n const allCodeConnectObjects = await getCodeConnectObjects(cmd, projectInfo, true)\n\n let nodesToCheck: NodeToPreview[]\n\n if (files && files.length > 0) {\n nodesToCheck = await collectNodesToPreview(files, allCodeConnectObjects, dir, cmd)\n } else {\n logger.info('Previewing all local Code Connect files...')\n nodesToCheck = []\n for (const doc of allCodeConnectObjects) {\n const parsed = parseFigmaNode(cmd.verbose, doc, true)\n if (parsed) {\n nodesToCheck.push({\n fileKey: parsed.fileKey,\n nodeId: parsed.nodeId,\n url: doc.figmaNode,\n filePath: path.relative(dir, doc._codeConnectFilePath || ''),\n })\n }\n }\n }\n\n if (nodesToCheck.length === 0) {\n exitWithError('No valid Code Connect files found to preview')\n }\n\n logger.info(`Previewing ${nodesToCheck.length} component(s)...`)\n\n // Group nodes by fileKey for batching\n const nodesByFileKey: Record<string, NodeToPreview[]> = {}\n for (const node of nodesToCheck) {\n if (!nodesByFileKey[node.fileKey]) {\n nodesByFileKey[node.fileKey] = []\n }\n nodesByFileKey[node.fileKey].push(node)\n }\n\n const results: PreviewResult[] = []\n\n for (const [fileKey, nodes] of Object.entries(nodesByFileKey)) {\n const baseApiUrl = getApiUrl(nodes[0].url, cmd.apiUrl || projectInfo.config.apiUrl)\n const allNodeIds = nodes.map((n) => n.nodeId)\n // Deduplicate — multiple figma.connect() calls may share the same nodeId;\n // the individual templates are sent in figmaDocs and the server iterates per-template.\n const nodeIds = [...new Set(allNodeIds)]\n\n // Only send templates from the requested files — other files' templates\n // for the same nodeId exist on the server and shouldn't be rendered individually.\n const requestedFilePaths = new Set(nodes.map((n) => path.resolve(dir, n.filePath)))\n const requiredTemplates = filterTemplatesForNodes(allNodeIds, allCodeConnectObjects).filter(\n (t) => requestedFilePaths.has(path.resolve(t._codeConnectFilePath || '')),\n )\n const figmaDocs: Record<string, any[]> = { all: requiredTemplates }\n\n try {\n const response = await request.post<{\n status: number\n error: boolean\n meta: {\n results: Array<{\n nodeId: string\n nodeUrl: string\n snippet?: string\n language?: string\n component?: string\n error?: string\n }>\n }\n }>(\n `${baseApiUrl}/code_connect/preview_snippets?file_key=${fileKey}`,\n { nodeIds, figmaDocs },\n { headers: getHeaders(accessToken) },\n )\n\n if (response.response.status === 200 && response.data.meta?.results) {\n // Track match index per nodeId so duplicate node IDs get the correct file attribution.\n // The server returns results in the same order as the templates we sent.\n const nodeMatchIndex: Record<string, number> = {}\n\n for (const result of response.data.meta.results) {\n const idx = nodeMatchIndex[result.nodeId] ?? 0\n const matchingNodes = nodes.filter((n) => n.nodeId === result.nodeId)\n const node = matchingNodes[idx] || matchingNodes[0]\n nodeMatchIndex[result.nodeId] = idx + 1\n\n results.push({\n url: node?.url || result.nodeUrl,\n nodeId: result.nodeId,\n filePath: node?.filePath || '',\n success: !result.error && !!result.snippet,\n snippet: result.snippet,\n language: result.language,\n component: result.component,\n error: result.error || (!result.snippet ? 'No snippet returned by server' : undefined),\n })\n }\n } else {\n for (const node of nodes) {\n results.push({\n url: node.url,\n nodeId: node.nodeId,\n filePath: node.filePath,\n success: false,\n error: `API request failed with status ${response.response.status}`,\n })\n }\n }\n } catch (err) {\n const errorMsg = isFetchError(err)\n ? err.data?.message || err.response.statusText\n : String(err)\n logger.error(`Failed to preview components in file ${fileKey}: ${errorMsg}`)\n for (const node of nodes) {\n results.push({\n url: node.url,\n nodeId: node.nodeId,\n filePath: node.filePath,\n success: false,\n error: errorMsg,\n })\n }\n }\n }\n\n // Validate snippet syntax\n for (const result of results) {\n if (result.success && result.snippet) {\n const prettierValid = await isPrettierParseable(result.snippet, result.language)\n if (!prettierValid) {\n // Prettier can parse this language but failed — mark as error\n result.success = false\n result.error = 'Snippet has syntax errors and may not be valid code'\n }\n }\n }\n\n if (outputFormat === 'json') {\n console.log(JSON.stringify(results, null, 2))\n } else {\n const formattedResults: PreviewResult[] = await Promise.all(\n results.map(async (result) => {\n if (result.success && result.snippet) {\n const formattedSnippet = await formatSnippet(result.snippet, result.language)\n return { ...result, snippet: formattedSnippet }\n }\n return result\n }),\n )\n displayResults(formattedResults)\n }\n\n if (results.every((r) => !r.success)) {\n process.exit(1)\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { CodeConnectJSON } from './figma_connect';
2
+ import { CodeConnectConfig } from './project';
3
+ /**
4
+ * Parses a `.figma.batch.json` file and produces CodeConnectJSON objects.
5
+ *
6
+ * For each component entry, delegates to `parseRawFile` with batch overrides
7
+ * that prepend `window['__FIGMA_BATCH'] = <data>` to the template. The figma
8
+ * runtime exposes this as `figma.batch`.
9
+ */
10
+ export declare function parseBatchFile(filePath: string, label: string | undefined, config?: CodeConnectConfig, dir?: string): CodeConnectJSON[];
11
+ //# sourceMappingURL=batch_templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch_templates.d.ts","sourceRoot":"","sources":["../../src/connect/batch_templates.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAuC7C;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,GAAG,CAAC,EAAE,MAAM,GACX,eAAe,EAAE,CA8CnB"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseBatchFile = parseBatchFile;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const raw_templates_1 = require("./raw_templates");
10
+ /**
11
+ * Resolves and validates the template file path referenced by a batch group.
12
+ * Returns the absolute path or throws a descriptive error.
13
+ */
14
+ function validateBatchTemplatePath(templateFile, batchDir, batchFilePath) {
15
+ const templatePath = path_1.default.resolve(batchDir, templateFile);
16
+ if (!fs_1.default.existsSync(templatePath)) {
17
+ throw new Error(`Template file not found: ${templatePath} (referenced from ${batchFilePath})`);
18
+ }
19
+ if (!templatePath.endsWith('.figma.batch.ts') && !templatePath.endsWith('.figma.batch.js')) {
20
+ throw new Error(`Template file must have a .figma.batch.ts or .figma.batch.js extension: ${templatePath}`);
21
+ }
22
+ return templatePath;
23
+ }
24
+ /**
25
+ * Parses a `.figma.batch.json` file and produces CodeConnectJSON objects.
26
+ *
27
+ * For each component entry, delegates to `parseRawFile` with batch overrides
28
+ * that prepend `window['__FIGMA_BATCH'] = <data>` to the template. The figma
29
+ * runtime exposes this as `figma.batch`.
30
+ */
31
+ function parseBatchFile(filePath, label, config, dir) {
32
+ const raw = fs_1.default.readFileSync(filePath, 'utf-8');
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(raw);
36
+ }
37
+ catch (e) {
38
+ throw new Error(`Failed to parse JSON in ${filePath}: ${e}`);
39
+ }
40
+ // Support both single group (object) and multiple groups (array)
41
+ const groups = Array.isArray(parsed) ? parsed : [parsed];
42
+ const results = [];
43
+ const batchDir = path_1.default.dirname(filePath);
44
+ for (const group of groups) {
45
+ if (!group.templateFile) {
46
+ throw new Error(`Missing required field "templateFile" in ${filePath}`);
47
+ }
48
+ if (!Array.isArray(group.components) || group.components.length === 0) {
49
+ throw new Error(`"components" must be a non-empty array in ${filePath}`);
50
+ }
51
+ const templatePath = validateBatchTemplatePath(group.templateFile, batchDir, filePath);
52
+ for (let i = 0; i < group.components.length; i++) {
53
+ const entry = group.components[i];
54
+ if (!entry.url) {
55
+ const entryName = entry.name || entry.component || `index ${i}`;
56
+ throw new Error(`Missing required field "url" in entry "${entryName}" in ${filePath}`);
57
+ }
58
+ const doc = (0, raw_templates_1.parseRawFile)(templatePath, label, config, dir, {
59
+ url: entry.url,
60
+ source: entry.source,
61
+ component: entry.component,
62
+ batchData: entry,
63
+ batchFilePath: filePath,
64
+ });
65
+ results.push(doc);
66
+ }
67
+ }
68
+ return results;
69
+ }
70
+ //# sourceMappingURL=batch_templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch_templates.js","sourceRoot":"","sources":["../../src/connect/batch_templates.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { CodeConnectJSON } from './figma_connect'\nimport { CodeConnectConfig } from './project'\nimport { parseRawFile } from './raw_templates'\n\ninterface BatchComponentEntry {\n url: string\n source?: string\n component?: string\n [key: string]: any\n}\n\ninterface BatchGroup {\n templateFile: string\n components: BatchComponentEntry[]\n}\n\n/**\n * Resolves and validates the template file path referenced by a batch group.\n * Returns the absolute path or throws a descriptive error.\n */\nfunction validateBatchTemplatePath(\n templateFile: string,\n batchDir: string,\n batchFilePath: string,\n): string {\n const templatePath = path.resolve(batchDir, templateFile)\n\n if (!fs.existsSync(templatePath)) {\n throw new Error(`Template file not found: ${templatePath} (referenced from ${batchFilePath})`)\n }\n\n if (!templatePath.endsWith('.figma.batch.ts') && !templatePath.endsWith('.figma.batch.js')) {\n throw new Error(\n `Template file must have a .figma.batch.ts or .figma.batch.js extension: ${templatePath}`,\n )\n }\n\n return templatePath\n}\n\n/**\n * Parses a `.figma.batch.json` file and produces CodeConnectJSON objects.\n *\n * For each component entry, delegates to `parseRawFile` with batch overrides\n * that prepend `window['__FIGMA_BATCH'] = <data>` to the template. The figma\n * runtime exposes this as `figma.batch`.\n */\nexport function parseBatchFile(\n filePath: string,\n label: string | undefined,\n config?: CodeConnectConfig,\n dir?: string,\n): CodeConnectJSON[] {\n const raw = fs.readFileSync(filePath, 'utf-8')\n let parsed: any\n try {\n parsed = JSON.parse(raw)\n } catch (e) {\n throw new Error(`Failed to parse JSON in ${filePath}: ${e}`)\n }\n\n // Support both single group (object) and multiple groups (array)\n const groups: BatchGroup[] = Array.isArray(parsed) ? parsed : [parsed]\n\n const results: CodeConnectJSON[] = []\n const batchDir = path.dirname(filePath)\n\n for (const group of groups) {\n if (!group.templateFile) {\n throw new Error(`Missing required field \"templateFile\" in ${filePath}`)\n }\n if (!Array.isArray(group.components) || group.components.length === 0) {\n throw new Error(`\"components\" must be a non-empty array in ${filePath}`)\n }\n\n const templatePath = validateBatchTemplatePath(group.templateFile, batchDir, filePath)\n\n for (let i = 0; i < group.components.length; i++) {\n const entry = group.components[i]\n\n if (!entry.url) {\n const entryName = entry.name || entry.component || `index ${i}`\n throw new Error(`Missing required field \"url\" in entry \"${entryName}\" in ${filePath}`)\n }\n\n const doc = parseRawFile(templatePath, label, config, dir, {\n url: entry.url,\n source: entry.source,\n component: entry.component,\n batchData: entry,\n batchFilePath: filePath,\n })\n\n results.push(doc)\n }\n }\n\n return results\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/connect/create.ts"],"names":[],"mappings":"AAUA,OAAO,EAAqC,WAAW,EAAE,MAAM,WAAW,CAAA;AAW1E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEjD,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,EAAE,WAAW,CAAA;CACjB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,UAQlD;AAED,wBAAsB,wBAAwB,CAAC,EAC7C,WAAW,EACX,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,GAAG,GACJ,EAAE,gBAAgB,iBA+HlB"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/connect/create.ts"],"names":[],"mappings":"AAUA,OAAO,EAAqC,WAAW,EAAE,MAAM,WAAW,CAAA;AAY1E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEjD,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,EAAE,WAAW,CAAA;CACjB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,UAQlD;AAED,wBAAsB,wBAAwB,CAAC,EAC7C,WAAW,EACX,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,GAAG,GACJ,EAAE,gBAAgB,iBA0IlB"}
@@ -14,6 +14,7 @@ const create_1 = require("../react/create");
14
14
  const parser_executable_types_1 = require("./parser_executable_types");
15
15
  const zod_validation_error_1 = require("zod-validation-error");
16
16
  const create_2 = require("../html/create");
17
+ const create_template_1 = require("./create_template");
17
18
  const fetch_1 = require("../common/fetch");
18
19
  function normalizeComponentName(name) {
19
20
  // Convert the string to PascalCase and ensure first character is not a digit
@@ -84,6 +85,18 @@ async function createCodeConnectFromUrl({ accessToken, figmaNodeUrl, outFile, ou
84
85
  };
85
86
  result = await (0, create_2.createHtmlCodeConnect)(payload);
86
87
  }
88
+ else if (!projectInfo.config.parser) {
89
+ const filename = (0, create_template_1.createTemplateCodeConnect)({
90
+ component: componentPayload,
91
+ normalizedName,
92
+ destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),
93
+ destinationFile: outFile,
94
+ });
95
+ result = {
96
+ createdFiles: [{ filePath: filename }],
97
+ messages: [],
98
+ };
99
+ }
87
100
  else {
88
101
  try {
89
102
  const payload = {
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/connect/create.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n exitWithFeedbackMessage,\n findComponentsInDocument,\n parseFileKey,\n parseNodeIds,\n} from './helpers'\nimport fs from 'fs'\nimport { FigmaRestApi, getApiUrl, getHeaders } from './figma_rest_api'\nimport { exitWithError, logger } from '../common/logging'\nimport { callParser, handleMessages } from './parser_executables'\nimport { CodeConnectExecutableParserConfig, ProjectInfo } from './project'\nimport { createReactCodeConnect } from '../react/create'\nimport { z } from 'zod'\nimport {\n CreateRequestPayload,\n CreateRequestPayloadMulti,\n CreateResponsePayload,\n} from './parser_executable_types'\nimport { fromError } from 'zod-validation-error'\nimport { createHtmlCodeConnect } from '../html/create'\nimport { isFetchError, request } from '../common/fetch'\nimport { BaseCommand } from '../commands/connect'\n\ninterface GenerateDocsArgs {\n accessToken: string\n figmaNodeUrl: string\n outFile: string\n outDir: string\n projectInfo: ProjectInfo\n cmd: BaseCommand\n}\n\nexport function normalizeComponentName(name: string) {\n // Convert the string to PascalCase and ensure first character is not a digit\n return name\n .replace(/[^a-zA-Z0-9]/g, ' ')\n .split(' ')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join('')\n .replace(/^[0-9]/, '_$&')\n}\n\nexport async function createCodeConnectFromUrl({\n accessToken,\n figmaNodeUrl,\n outFile,\n outDir,\n projectInfo,\n cmd,\n}: GenerateDocsArgs) {\n try {\n const fileKey = parseFileKey(figmaNodeUrl)\n const nodeIds = parseNodeIds([figmaNodeUrl])\n\n const apiUrl =\n getApiUrl(figmaNodeUrl ?? '', cmd.apiUrl || projectInfo.config.apiUrl) +\n `/code_connect/${fileKey}/cli_data?ids=${nodeIds.join(',')}`\n\n if (nodeIds.length === 0) {\n exitWithError(\n `Invalid figma node URL: the provided url \"${figmaNodeUrl}\" does not contain a node-id`,\n )\n } else if (nodeIds.length > 1) {\n exitWithError(\n `Invalid figma node URL: the provided url \"${figmaNodeUrl}\" contains more than one node-id`,\n )\n }\n\n logger.info('Fetching component information from Figma...')\n const response = process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE\n ? {\n response: { status: 200 },\n data: JSON.parse(\n fs.readFileSync(process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE, 'utf-8'),\n ) as { document: FigmaRestApi.Node },\n }\n : await request.get<{ document: FigmaRestApi.Node }>(apiUrl, {\n headers: getHeaders(accessToken),\n })\n\n if (response.response.status === 200) {\n logger.info('Parsing response')\n const component = findComponentsInDocument(response.data.document, nodeIds)[0]\n if (component === undefined) {\n exitWithError('Could not find a component in the provided URL')\n }\n const normalizedName = normalizeComponentName(component.name)\n\n const componentPayload = {\n figmaNodeUrl,\n id: component.id,\n name: component.name,\n normalizedName,\n type: component.type,\n componentPropertyDefinitions: component.componentPropertyDefinitions,\n }\n\n logger.info('Generating Code Connect files...')\n\n let result: z.infer<typeof CreateResponsePayload>\n\n if (projectInfo.config.parser === 'react') {\n const payload: CreateRequestPayloadMulti = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n normalizedName,\n figmaConnections: [{ component: componentPayload }],\n config: projectInfo.config,\n }\n result = await createReactCodeConnect(payload)\n } else if (projectInfo.config.parser === 'html') {\n const payload: CreateRequestPayloadMulti = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n normalizedName,\n figmaConnections: [{ component: componentPayload }],\n config: projectInfo.config,\n }\n result = await createHtmlCodeConnect(payload)\n } else {\n try {\n const payload: CreateRequestPayload = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n component: componentPayload,\n config: projectInfo.config,\n verbose: cmd.verbose,\n }\n const stdout = await callParser(\n // We use `as` because the React parser makes the types difficult\n // TODO remove once React is an executable parser\n projectInfo.config as CodeConnectExecutableParserConfig,\n payload,\n projectInfo.absPath,\n )\n\n result = CreateResponsePayload.parse(stdout)\n } catch (e) {\n throw fromError(e)\n }\n }\n\n const { hasErrors } = handleMessages(result.messages)\n\n if (hasErrors) {\n exitWithError('Errors encountered calling parser, exiting')\n }\n\n logger.info('Code Connect files generated successfully:')\n result.createdFiles.forEach((file) => {\n logger.info(`${file.filePath}`)\n })\n } else {\n logger.error(\n `Failed to get node information from Figma with status: ${response.response.status}`,\n )\n logger.debug('Failed to get node information from Figma with Body:', response.data)\n }\n } catch (err) {\n if (isFetchError(err)) {\n if (err.response) {\n logger.error(\n `Failed to get node data from Figma (${err.response.status}): ${err.response.status} ${err.data?.err ?? err.data?.message}`,\n )\n } else {\n logger.error(`Failed to get node data from Figma: ${err.message}`)\n }\n logger.debug(JSON.stringify(err.data))\n } else {\n logger.error(`Failed to create: ${err}`)\n }\n exitWithFeedbackMessage(1)\n }\n}\n"]}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/connect/create.ts"],"names":[],"mappings":";;;;;AAiCA,wDAQC;AAED,4DAiJC;AA5LD,uCAKkB;AAClB,4CAAmB;AACnB,qDAAsE;AACtE,+CAAyD;AACzD,6DAAiE;AAEjE,4CAAwD;AAExD,uEAIkC;AAClC,+DAAgD;AAChD,2CAAsD;AACtD,uDAA6D;AAC7D,2CAAuD;AAYvD,SAAgB,sBAAsB,CAAC,IAAY;IACjD,6EAA6E;IAC7E,OAAO,IAAI;SACR,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC3D,IAAI,CAAC,EAAE,CAAC;SACR,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;AAC7B,CAAC;AAEM,KAAK,UAAU,wBAAwB,CAAC,EAC7C,WAAW,EACX,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,GAAG,GACc;IACjB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,sBAAY,EAAC,YAAY,CAAC,CAAA;QAC1C,MAAM,OAAO,GAAG,IAAA,sBAAY,EAAC,CAAC,YAAY,CAAC,CAAC,CAAA;QAE5C,MAAM,MAAM,GACV,IAAA,0BAAS,EAAC,YAAY,IAAI,EAAE,EAAE,GAAG,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;YACtE,iBAAiB,OAAO,iBAAiB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;QAE9D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAA,uBAAa,EACX,6CAA6C,YAAY,8BAA8B,CACxF,CAAA;QACH,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,IAAA,uBAAa,EACX,6CAA6C,YAAY,kCAAkC,CAC5F,CAAA;QACH,CAAC;QAED,gBAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAA;QAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC;YAChE,CAAC,CAAC;gBACE,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;gBACzB,IAAI,EAAE,IAAI,CAAC,KAAK,CACd,YAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,OAAO,CAAC,CACzC;aACrC;YACH,CAAC,CAAC,MAAM,eAAO,CAAC,GAAG,CAAkC,MAAM,EAAE;gBACzD,OAAO,EAAE,IAAA,2BAAU,EAAC,WAAW,CAAC;aACjC,CAAC,CAAA;QAEN,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACrC,gBAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;YAC/B,MAAM,SAAS,GAAG,IAAA,kCAAwB,EAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;YAC9E,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAA,uBAAa,EAAC,gDAAgD,CAAC,CAAA;YACjE,CAAC;YACD,MAAM,cAAc,GAAG,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAE7D,MAAM,gBAAgB,GAAG;gBACvB,YAAY;gBACZ,EAAE,EAAE,SAAS,CAAC,EAAE;gBAChB,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,cAAc;gBACd,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,4BAA4B,EAAE,SAAS,CAAC,4BAA4B;aACrE,CAAA;YAED,gBAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YAE/C,IAAI,MAA6C,CAAA;YAEjD,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC1C,MAAM,OAAO,GAA8B;oBACzC,IAAI,EAAE,QAAQ;oBACd,cAAc,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE;oBAC/D,eAAe,EAAE,OAAO;oBACxB,cAAc;oBACd,gBAAgB,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;oBACnD,MAAM,EAAE,WAAW,CAAC,MAAM;iBAC3B,CAAA;gBACD,MAAM,GAAG,MAAM,IAAA,+BAAsB,EAAC,OAAO,CAAC,CAAA;YAChD,CAAC;iBAAM,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAChD,MAAM,OAAO,GAA8B;oBACzC,IAAI,EAAE,QAAQ;oBACd,cAAc,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE;oBAC/D,eAAe,EAAE,OAAO;oBACxB,cAAc;oBACd,gBAAgB,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;oBACnD,MAAM,EAAE,WAAW,CAAC,MAAM;iBAC3B,CAAA;gBACD,MAAM,GAAG,MAAM,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAA;YAC/C,CAAC;iBAAM,IAAI,CAAE,WAAW,CAAC,MAA8B,CAAC,MAAM,EAAE,CAAC;gBAC/D,MAAM,QAAQ,GAAG,IAAA,2CAAyB,EAAC;oBACzC,SAAS,EAAE,gBAAgB;oBAC3B,cAAc;oBACd,cAAc,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE;oBAC/D,eAAe,EAAE,OAAO;iBACzB,CAAC,CAAA;gBACF,MAAM,GAAG;oBACP,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;oBACtC,QAAQ,EAAE,EAAE;iBACb,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC;oBACH,MAAM,OAAO,GAAyB;wBACpC,IAAI,EAAE,QAAQ;wBACd,cAAc,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE;wBAC/D,eAAe,EAAE,OAAO;wBACxB,SAAS,EAAE,gBAAgB;wBAC3B,MAAM,EAAE,WAAW,CAAC,MAAM;wBAC1B,OAAO,EAAE,GAAG,CAAC,OAAO;qBACrB,CAAA;oBACD,MAAM,MAAM,GAAG,MAAM,IAAA,+BAAU;oBAC7B,iEAAiE;oBACjE,iDAAiD;oBACjD,WAAW,CAAC,MAA2C,EACvD,OAAO,EACP,WAAW,CAAC,OAAO,CACpB,CAAA;oBAED,MAAM,GAAG,+CAAqB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC9C,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,MAAM,IAAA,gCAAS,EAAC,CAAC,CAAC,CAAA;gBACpB,CAAC;YACH,CAAC;YAED,MAAM,EAAE,SAAS,EAAE,GAAG,IAAA,mCAAc,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YAErD,IAAI,SAAS,EAAE,CAAC;gBACd,IAAA,uBAAa,EAAC,4CAA4C,CAAC,CAAA;YAC7D,CAAC;YAED,gBAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAA;YACzD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBACnC,gBAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;YACjC,CAAC,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,gBAAM,CAAC,KAAK,CACV,0DAA0D,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CACrF,CAAA;YACD,gBAAM,CAAC,KAAK,CAAC,sDAAsD,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QACrF,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,IAAA,oBAAY,EAAC,GAAG,CAAC,EAAE,CAAC;YACtB,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjB,gBAAM,CAAC,KAAK,CACV,uCAAuC,GAAG,CAAC,QAAQ,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,CAC5H,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,gBAAM,CAAC,KAAK,CAAC,uCAAuC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YACpE,CAAC;YACD,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;QACxC,CAAC;aAAM,CAAC;YACN,gBAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAA;QAC1C,CAAC;QACD,IAAA,iCAAuB,EAAC,CAAC,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC","sourcesContent":["import {\n exitWithFeedbackMessage,\n findComponentsInDocument,\n parseFileKey,\n parseNodeIds,\n} from './helpers'\nimport fs from 'fs'\nimport { FigmaRestApi, getApiUrl, getHeaders } from './figma_rest_api'\nimport { exitWithError, logger } from '../common/logging'\nimport { callParser, handleMessages } from './parser_executables'\nimport { CodeConnectExecutableParserConfig, ProjectInfo } from './project'\nimport { createReactCodeConnect } from '../react/create'\nimport { z } from 'zod'\nimport {\n CreateRequestPayload,\n CreateRequestPayloadMulti,\n CreateResponsePayload,\n} from './parser_executable_types'\nimport { fromError } from 'zod-validation-error'\nimport { createHtmlCodeConnect } from '../html/create'\nimport { createTemplateCodeConnect } from './create_template'\nimport { isFetchError, request } from '../common/fetch'\nimport { BaseCommand } from '../commands/connect'\n\ninterface GenerateDocsArgs {\n accessToken: string\n figmaNodeUrl: string\n outFile: string\n outDir: string\n projectInfo: ProjectInfo\n cmd: BaseCommand\n}\n\nexport function normalizeComponentName(name: string) {\n // Convert the string to PascalCase and ensure first character is not a digit\n return name\n .replace(/[^a-zA-Z0-9]/g, ' ')\n .split(' ')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join('')\n .replace(/^[0-9]/, '_$&')\n}\n\nexport async function createCodeConnectFromUrl({\n accessToken,\n figmaNodeUrl,\n outFile,\n outDir,\n projectInfo,\n cmd,\n}: GenerateDocsArgs) {\n try {\n const fileKey = parseFileKey(figmaNodeUrl)\n const nodeIds = parseNodeIds([figmaNodeUrl])\n\n const apiUrl =\n getApiUrl(figmaNodeUrl ?? '', cmd.apiUrl || projectInfo.config.apiUrl) +\n `/code_connect/${fileKey}/cli_data?ids=${nodeIds.join(',')}`\n\n if (nodeIds.length === 0) {\n exitWithError(\n `Invalid figma node URL: the provided url \"${figmaNodeUrl}\" does not contain a node-id`,\n )\n } else if (nodeIds.length > 1) {\n exitWithError(\n `Invalid figma node URL: the provided url \"${figmaNodeUrl}\" contains more than one node-id`,\n )\n }\n\n logger.info('Fetching component information from Figma...')\n const response = process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE\n ? {\n response: { status: 200 },\n data: JSON.parse(\n fs.readFileSync(process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE, 'utf-8'),\n ) as { document: FigmaRestApi.Node },\n }\n : await request.get<{ document: FigmaRestApi.Node }>(apiUrl, {\n headers: getHeaders(accessToken),\n })\n\n if (response.response.status === 200) {\n logger.info('Parsing response')\n const component = findComponentsInDocument(response.data.document, nodeIds)[0]\n if (component === undefined) {\n exitWithError('Could not find a component in the provided URL')\n }\n const normalizedName = normalizeComponentName(component.name)\n\n const componentPayload = {\n figmaNodeUrl,\n id: component.id,\n name: component.name,\n normalizedName,\n type: component.type,\n componentPropertyDefinitions: component.componentPropertyDefinitions,\n }\n\n logger.info('Generating Code Connect files...')\n\n let result: z.infer<typeof CreateResponsePayload>\n\n if (projectInfo.config.parser === 'react') {\n const payload: CreateRequestPayloadMulti = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n normalizedName,\n figmaConnections: [{ component: componentPayload }],\n config: projectInfo.config,\n }\n result = await createReactCodeConnect(payload)\n } else if (projectInfo.config.parser === 'html') {\n const payload: CreateRequestPayloadMulti = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n normalizedName,\n figmaConnections: [{ component: componentPayload }],\n config: projectInfo.config,\n }\n result = await createHtmlCodeConnect(payload)\n } else if (!(projectInfo.config as { parser?: string }).parser) {\n const filename = createTemplateCodeConnect({\n component: componentPayload,\n normalizedName,\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n })\n result = {\n createdFiles: [{ filePath: filename }],\n messages: [],\n }\n } else {\n try {\n const payload: CreateRequestPayload = {\n mode: 'CREATE',\n destinationDir: outDir ?? process.env.INIT_CWD ?? process.cwd(),\n destinationFile: outFile,\n component: componentPayload,\n config: projectInfo.config,\n verbose: cmd.verbose,\n }\n const stdout = await callParser(\n // We use `as` because the React parser makes the types difficult\n // TODO remove once React is an executable parser\n projectInfo.config as CodeConnectExecutableParserConfig,\n payload,\n projectInfo.absPath,\n )\n\n result = CreateResponsePayload.parse(stdout)\n } catch (e) {\n throw fromError(e)\n }\n }\n\n const { hasErrors } = handleMessages(result.messages)\n\n if (hasErrors) {\n exitWithError('Errors encountered calling parser, exiting')\n }\n\n logger.info('Code Connect files generated successfully:')\n result.createdFiles.forEach((file) => {\n logger.info(`${file.filePath}`)\n })\n } else {\n logger.error(\n `Failed to get node information from Figma with status: ${response.response.status}`,\n )\n logger.debug('Failed to get node information from Figma with Body:', response.data)\n }\n } catch (err) {\n if (isFetchError(err)) {\n if (err.response) {\n logger.error(\n `Failed to get node data from Figma (${err.response.status}): ${err.response.status} ${err.data?.err ?? err.data?.message}`,\n )\n } else {\n logger.error(`Failed to get node data from Figma: ${err.message}`)\n }\n logger.debug(JSON.stringify(err.data))\n } else {\n logger.error(`Failed to create: ${err}`)\n }\n exitWithFeedbackMessage(1)\n }\n}\n"]}