@figma/code-connect 1.4.7 → 1.4.9

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 (47) hide show
  1. package/dist/commands/connect.d.ts +3 -0
  2. package/dist/commands/connect.d.ts.map +1 -1
  3. package/dist/commands/connect.js +140 -40
  4. package/dist/commands/connect.js.map +1 -1
  5. package/dist/commands/connect_template.d.ts.map +1 -1
  6. package/dist/commands/connect_template.js +9 -0
  7. package/dist/commands/connect_template.js.map +1 -1
  8. package/dist/common/logging.d.ts.map +1 -1
  9. package/dist/common/logging.js +13 -6
  10. package/dist/common/logging.js.map +1 -1
  11. package/dist/common/updates.d.ts.map +1 -1
  12. package/dist/common/updates.js +2 -2
  13. package/dist/common/updates.js.map +1 -1
  14. package/dist/connect/api.d.ts +34 -1
  15. package/dist/connect/api.d.ts.map +1 -1
  16. package/dist/connect/api.js.map +1 -1
  17. package/dist/connect/intrinsics.d.ts +1 -0
  18. package/dist/connect/intrinsics.d.ts.map +1 -1
  19. package/dist/connect/intrinsics.js +29 -0
  20. package/dist/connect/intrinsics.js.map +1 -1
  21. package/dist/connect/migration_batch_helpers.d.ts +45 -0
  22. package/dist/connect/migration_batch_helpers.d.ts.map +1 -0
  23. package/dist/connect/migration_batch_helpers.js +439 -0
  24. package/dist/connect/migration_batch_helpers.js.map +1 -0
  25. package/dist/connect/migration_helpers.d.ts +4 -0
  26. package/dist/connect/migration_helpers.d.ts.map +1 -1
  27. package/dist/connect/migration_helpers.js +29 -5
  28. package/dist/connect/migration_helpers.js.map +1 -1
  29. package/dist/connect/parser_executables.d.ts.map +1 -1
  30. package/dist/connect/parser_executables.js +25 -13
  31. package/dist/connect/parser_executables.js.map +1 -1
  32. package/dist/connect/project.d.ts +2 -1
  33. package/dist/connect/project.d.ts.map +1 -1
  34. package/dist/connect/project.js +26 -24
  35. package/dist/connect/project.js.map +1 -1
  36. package/dist/connect/raw_templates.d.ts +10 -0
  37. package/dist/connect/raw_templates.d.ts.map +1 -1
  38. package/dist/connect/raw_templates.js +48 -14
  39. package/dist/connect/raw_templates.js.map +1 -1
  40. package/dist/connect/wizard/helpers.d.ts.map +1 -1
  41. package/dist/connect/wizard/helpers.js +7 -2
  42. package/dist/connect/wizard/helpers.js.map +1 -1
  43. package/dist/parser_scripts/get_swift_parser_dir.js +5 -5
  44. package/dist/parser_scripts/get_swift_parser_dir.js.map +1 -1
  45. package/figma-types-no-require.d.ts +229 -0
  46. package/figma-types.d.ts +12 -3
  47. package/package.json +7 -3
@@ -0,0 +1,439 @@
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.AUTO_BATCH_THRESHOLD = void 0;
7
+ exports.getBatchMigrationGroups = getBatchMigrationGroups;
8
+ exports.buildBatchMigration = buildBatchMigration;
9
+ exports.writeBatchTemplateFiles = writeBatchTemplateFiles;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const migration_helpers_1 = require("./migration_helpers");
13
+ exports.AUTO_BATCH_THRESHOLD = 10;
14
+ function uniqueValues(values) {
15
+ return new Set(values);
16
+ }
17
+ function getBatchMigrationGroups(docs, { batchAll = false, disabled = false, threshold = exports.AUTO_BATCH_THRESHOLD, } = {}) {
18
+ if (disabled) {
19
+ return new Map();
20
+ }
21
+ const docsBySourcePath = new Map();
22
+ for (const doc of docs) {
23
+ if (!doc._codeConnectFilePath) {
24
+ continue;
25
+ }
26
+ const sourcePath = path_1.default.resolve(doc._codeConnectFilePath);
27
+ docsBySourcePath.set(sourcePath, [...(docsBySourcePath.get(sourcePath) || []), doc]);
28
+ }
29
+ const batchGroups = new Map();
30
+ for (const [sourcePath, sourceDocs] of docsBySourcePath) {
31
+ if (batchAll || sourceDocs.length >= threshold) {
32
+ batchGroups.set(sourcePath, sourceDocs);
33
+ }
34
+ }
35
+ return batchGroups;
36
+ }
37
+ function allEqual(values) {
38
+ return values.every((value) => value === values[0]);
39
+ }
40
+ function isIdentifier(value) {
41
+ return !!value && /^[A-Za-z_$][\w$]*$/.test(value);
42
+ }
43
+ function escapeRegExp(value) {
44
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45
+ }
46
+ function applyReplacements(body, replacements) {
47
+ return replacements
48
+ .sort((a, b) => b.start - a.start)
49
+ .reduce((result, replacement) => {
50
+ return result.slice(0, replacement.start) + replacement.value + result.slice(replacement.end);
51
+ }, body);
52
+ }
53
+ function removeUnusedSimpleConsts(body) {
54
+ let previous;
55
+ let next = body;
56
+ const simpleConstRegex = /^const\s+([A-Za-z_$][\w$]*)\s*=\s*(?:"[^"\n]*"|'[^'\n]*'|figma\.helpers\.react\.identifier\(["'][^"'\n]+["']\)|[A-Za-z_$][\w$]*|true|false|-?\d+(?:\.\d+)?)\s*\n/gm;
57
+ do {
58
+ previous = next;
59
+ next = next.replace(simpleConstRegex, (match, name, offset) => {
60
+ const rest = next.slice(offset + match.length);
61
+ const isReferenced = new RegExp(`\\b${escapeRegExp(name)}\\b`).test(rest);
62
+ return isReferenced ? match : '';
63
+ });
64
+ } while (next !== previous);
65
+ return next;
66
+ }
67
+ function deriveMetadata(docs) {
68
+ const sources = docs.map((doc) => doc.source || undefined);
69
+ const components = docs.map((doc) => doc.component || undefined);
70
+ const entries = docs.map((doc) => ({ url: doc.figmaNode }));
71
+ const comments = [];
72
+ const metadataFields = [
73
+ { name: 'source', values: sources },
74
+ { name: 'component', values: components },
75
+ ];
76
+ for (const field of metadataFields) {
77
+ const hasAny = field.values.some(Boolean);
78
+ const hasAll = field.values.every(Boolean);
79
+ const unique = uniqueValues(field.values);
80
+ if (hasAny && !hasAll) {
81
+ return {
82
+ ok: false,
83
+ reason: `Cannot batch: some docs have ${field.name} metadata and others do not`,
84
+ };
85
+ }
86
+ if (!hasAny) {
87
+ continue;
88
+ }
89
+ if (unique.size === 1) {
90
+ comments.push(`// ${field.name}=${field.values[0]}`);
91
+ }
92
+ else {
93
+ docs.forEach((doc, index) => {
94
+ const value = doc[field.name];
95
+ if (value) {
96
+ entries[index][field.name] = value;
97
+ }
98
+ });
99
+ }
100
+ }
101
+ return { ok: true, comments, entries };
102
+ }
103
+ function parseImport(importString) {
104
+ const named = importString.match(/^import\s+\{\s*([A-Za-z_$][\w$]*)\s*\}\s+from\s+["']([^"']+)["']\s*;?$/);
105
+ if (named) {
106
+ return { kind: 'named', symbol: named[1], importPath: named[2], raw: importString };
107
+ }
108
+ const defaultImport = importString.match(/^import\s+([A-Za-z_$][\w$]*)\s+from\s+["']([^"']+)["']\s*;?$/);
109
+ if (defaultImport) {
110
+ return {
111
+ kind: 'default',
112
+ symbol: defaultImport[1],
113
+ importPath: defaultImport[2],
114
+ raw: importString,
115
+ };
116
+ }
117
+ return null;
118
+ }
119
+ function tryIdSwap(candidates) {
120
+ const matches = candidates.map((candidate) => candidate.body.match(/\bid:\s*(['"])([^'"\\]+)\1/));
121
+ if (matches.some((match) => !match)) {
122
+ return candidates;
123
+ }
124
+ const ids = matches.map((match) => match[2]);
125
+ if (uniqueValues(ids).size <= 1) {
126
+ return candidates;
127
+ }
128
+ return candidates.map((candidate, index) => ({
129
+ ...candidate,
130
+ body: candidate.body.replace(matches[index][0], 'id: figma.batch.id'),
131
+ params: { ...candidate.params, id: ids[index] },
132
+ }));
133
+ }
134
+ function tryImportSwap(candidates) {
135
+ const importLists = candidates.map((candidate) => candidate.doc.templateData?.imports || []);
136
+ if (importLists.some((imports) => imports.length !== 1)) {
137
+ return candidates;
138
+ }
139
+ const importStrings = importLists.map((imports) => imports[0]);
140
+ if (uniqueValues(importStrings).size <= 1) {
141
+ return candidates;
142
+ }
143
+ const parsed = importStrings.map(parseImport);
144
+ if (parsed.some((info) => !info)) {
145
+ return candidates;
146
+ }
147
+ const imports = parsed;
148
+ if (!allEqual(imports.map((info) => info.kind))) {
149
+ return candidates;
150
+ }
151
+ const symbolVaries = uniqueValues(imports.map((info) => info.symbol)).size > 1;
152
+ const pathVaries = uniqueValues(imports.map((info) => info.importPath)).size > 1;
153
+ if (!symbolVaries && !pathVaries) {
154
+ return candidates;
155
+ }
156
+ return candidates.map((candidate, index) => {
157
+ const info = imports[index];
158
+ const symbol = symbolVaries ? '${figma.batch.componentName}' : info.symbol;
159
+ const importPath = pathVaries ? '${figma.batch.importPath}' : info.importPath;
160
+ const replacement = info.kind === 'named'
161
+ ? `\`import { ${symbol} } from "${importPath}"\``
162
+ : `\`import ${symbol} from "${importPath}"\``;
163
+ const encodedImport = JSON.stringify(info.raw);
164
+ if (!candidate.body.includes(encodedImport)) {
165
+ return candidate;
166
+ }
167
+ return {
168
+ ...candidate,
169
+ body: candidate.body.replace(encodedImport, replacement),
170
+ params: {
171
+ ...candidate.params,
172
+ ...(symbolVaries ? { componentName: info.symbol } : {}),
173
+ ...(pathVaries ? { importPath: info.importPath } : {}),
174
+ },
175
+ };
176
+ });
177
+ }
178
+ function templateLiteralTextRanges(content, contentStart) {
179
+ const ranges = [];
180
+ let textStart = 0;
181
+ let i = 0;
182
+ while (i < content.length) {
183
+ if (content[i] === '$' && content[i + 1] === '{') {
184
+ if (textStart < i) {
185
+ ranges.push({
186
+ start: contentStart + textStart,
187
+ end: contentStart + i,
188
+ text: content.slice(textStart, i),
189
+ });
190
+ }
191
+ i += 2;
192
+ let depth = 1;
193
+ while (i < content.length && depth > 0) {
194
+ if (content[i] === '{')
195
+ depth++;
196
+ if (content[i] === '}')
197
+ depth--;
198
+ i++;
199
+ }
200
+ textStart = i;
201
+ continue;
202
+ }
203
+ i++;
204
+ }
205
+ if (textStart < content.length) {
206
+ ranges.push({
207
+ start: contentStart + textStart,
208
+ end: contentStart + content.length,
209
+ text: content.slice(textStart),
210
+ });
211
+ }
212
+ return ranges;
213
+ }
214
+ function figmaCodeTextRanges(body) {
215
+ const ranges = [];
216
+ const regex = /figma\.code`([\s\S]*?)`/g;
217
+ let match;
218
+ while ((match = regex.exec(body))) {
219
+ const contentStart = match.index + 'figma.code`'.length;
220
+ ranges.push(...templateLiteralTextRanges(match[1], contentStart));
221
+ }
222
+ return ranges;
223
+ }
224
+ function replaceIdentifierInFigmaCode(body, identifier, replacement) {
225
+ const ranges = figmaCodeTextRanges(body);
226
+ const identifierRegex = new RegExp(`(^|[^A-Za-z0-9_$])${escapeRegExp(identifier)}(?=$|[^A-Za-z0-9_$])`, 'g');
227
+ const replacements = [];
228
+ for (const range of ranges) {
229
+ let match;
230
+ while ((match = identifierRegex.exec(range.text))) {
231
+ const prefixLength = match[1].length;
232
+ const start = range.start + match.index + prefixLength;
233
+ replacements.push({
234
+ start,
235
+ end: start + identifier.length,
236
+ value: replacement,
237
+ });
238
+ }
239
+ }
240
+ return applyReplacements(body, replacements);
241
+ }
242
+ function tryComponentSymbolSwap(candidates) {
243
+ let withNames = candidates;
244
+ if (withNames.some((candidate) => !candidate.params.componentName)) {
245
+ const ids = withNames.map((candidate) => candidate.params.id);
246
+ if (ids.every(isIdentifier) && uniqueValues(ids).size > 1) {
247
+ withNames = withNames.map((candidate) => ({
248
+ ...candidate,
249
+ params: { ...candidate.params, componentName: candidate.params.id },
250
+ }));
251
+ }
252
+ }
253
+ if (withNames.some((candidate) => !candidate.params.componentName)) {
254
+ return candidates;
255
+ }
256
+ const names = withNames.map((candidate) => candidate.params.componentName);
257
+ if (uniqueValues(names).size <= 1) {
258
+ return candidates;
259
+ }
260
+ return withNames.map((candidate, index) => ({
261
+ ...candidate,
262
+ body: replaceIdentifierInFigmaCode(candidate.body, names[index], '${figma.batch.componentName}'),
263
+ }));
264
+ }
265
+ function collectNamedScalars(body, kind) {
266
+ const ranges = figmaCodeTextRanges(body);
267
+ const regex = kind === 'string'
268
+ ? /([A-Za-z_$][\w$]*)\s*(=|:)\s*(['"])([^'"\\\n]+)\3/g
269
+ : /([A-Za-z_$][\w$]*)\s*(=|:)\s*(?:\{\s*)?(true|false|-?\d+(?:\.\d+)?)(?:\s*\})?(?=$|[^A-Za-z0-9_$.])/g;
270
+ const matches = [];
271
+ for (const range of ranges) {
272
+ let match;
273
+ while ((match = regex.exec(range.text))) {
274
+ const value = kind === 'string' ? match[4] : match[3];
275
+ const valueOffset = match[0].lastIndexOf(value);
276
+ matches.push({
277
+ key: match[1],
278
+ operator: match[2],
279
+ value,
280
+ valueStart: range.start + match.index + valueOffset,
281
+ valueEnd: range.start + match.index + valueOffset + value.length,
282
+ });
283
+ }
284
+ }
285
+ return matches;
286
+ }
287
+ function parseScalarValue(value, kind) {
288
+ if (kind === 'string') {
289
+ return value;
290
+ }
291
+ if (value === 'true') {
292
+ return true;
293
+ }
294
+ if (value === 'false') {
295
+ return false;
296
+ }
297
+ return Number(value);
298
+ }
299
+ function tryNamedScalarSwap(candidates, kind) {
300
+ const matchesByCandidate = candidates.map((candidate) => collectNamedScalars(candidate.body, kind));
301
+ const matchCount = matchesByCandidate[0]?.length || 0;
302
+ if (matchCount === 0 || matchesByCandidate.some((matches) => matches.length !== matchCount)) {
303
+ return candidates;
304
+ }
305
+ const replacementsByCandidate = candidates.map(() => []);
306
+ const fieldNames = new Set();
307
+ const nextCandidates = candidates.map((candidate) => ({
308
+ ...candidate,
309
+ params: { ...candidate.params },
310
+ }));
311
+ for (let matchIndex = 0; matchIndex < matchCount; matchIndex++) {
312
+ const matches = matchesByCandidate.map((matches) => matches[matchIndex]);
313
+ const first = matches[0];
314
+ if (matches.some((match) => match.key !== first.key || match.operator !== first.operator) ||
315
+ uniqueValues(matches.map((match) => match.value)).size <= 1) {
316
+ continue;
317
+ }
318
+ if (fieldNames.has(first.key) || first.key in nextCandidates[0].params) {
319
+ return candidates;
320
+ }
321
+ fieldNames.add(first.key);
322
+ matches.forEach((match, candidateIndex) => {
323
+ replacementsByCandidate[candidateIndex].push({
324
+ start: match.valueStart,
325
+ end: match.valueEnd,
326
+ value: `\${figma.batch.${first.key}}`,
327
+ });
328
+ nextCandidates[candidateIndex].params[first.key] = parseScalarValue(match.value, kind);
329
+ });
330
+ }
331
+ if (fieldNames.size === 0) {
332
+ return candidates;
333
+ }
334
+ return nextCandidates.map((candidate, index) => ({
335
+ ...candidate,
336
+ body: applyReplacements(candidate.body, replacementsByCandidate[index]),
337
+ }));
338
+ }
339
+ function buildBatchMigration(docs, { includeProps = false, useTypeScript = true, templateFile = './template.figma.batch.ts', } = {}) {
340
+ if (docs.length < 2) {
341
+ return { ok: false, reason: 'Cannot batch: at least two docs are required' };
342
+ }
343
+ if (docs.some((doc) => doc.variant && Object.keys(doc.variant).length > 0)) {
344
+ return { ok: false, reason: 'Cannot batch: variant Code Connect docs are not supported yet' };
345
+ }
346
+ if (docs.some((doc) => !doc.template)) {
347
+ return { ok: false, reason: 'Cannot batch: every doc must have a generated template' };
348
+ }
349
+ const metadata = deriveMetadata(docs);
350
+ if (!metadata.ok) {
351
+ return metadata;
352
+ }
353
+ let candidates = docs.map((doc, index) => ({
354
+ doc,
355
+ body: removeUnusedSimpleConsts((0, migration_helpers_1.prepareMigratedTemplateBody)(doc, includeProps, useTypeScript)),
356
+ params: metadata.entries[index],
357
+ }));
358
+ candidates = tryIdSwap(candidates);
359
+ candidates = tryImportSwap(candidates);
360
+ candidates = tryComponentSymbolSwap(candidates);
361
+ candidates = tryNamedScalarSwap(candidates, 'string');
362
+ candidates = tryNamedScalarSwap(candidates, 'numberBoolean');
363
+ if (!allEqual(candidates.map((candidate) => candidate.body))) {
364
+ return {
365
+ ok: false,
366
+ reason: 'Cannot batch: templates did not reduce to one compatible shape',
367
+ };
368
+ }
369
+ const templateBody = (0, migration_helpers_1.formatTemplate)(candidates[0].body);
370
+ const comments = metadata.comments.length > 0 ? `${metadata.comments.join('\n')}\n\n` : '';
371
+ return {
372
+ ok: true,
373
+ template: `${comments}${templateBody}`,
374
+ batchJson: {
375
+ templateFile,
376
+ components: candidates.map((candidate) => candidate.params),
377
+ },
378
+ };
379
+ }
380
+ function getSourceBasename(localSourcePath, docs) {
381
+ if (!localSourcePath) {
382
+ return (0, migration_helpers_1.getFilenameFromComponentName)(docs[0]?.component || 'batch');
383
+ }
384
+ const sourceBasename = path_1.default.basename(localSourcePath);
385
+ const codeConnectPattern = /\.(figma|figmadoc)(\.[^.]+)+$/;
386
+ return codeConnectPattern.test(sourceBasename)
387
+ ? sourceBasename.replace(codeConnectPattern, '')
388
+ : path_1.default.basename(localSourcePath, path_1.default.extname(localSourcePath));
389
+ }
390
+ function determineBatchOutputPaths(basename, outputDir, baseDir, localSourcePath, useTypeScript, filePathsCreated) {
391
+ const dir = outputDir || (localSourcePath ? path_1.default.dirname(localSourcePath) : baseDir);
392
+ const templateSuffix = useTypeScript ? '.figma.batch.ts' : '.figma.batch.js';
393
+ const getPaths = (name) => ({
394
+ templatePath: path_1.default.join(dir, `${name}${templateSuffix}`),
395
+ batchPath: path_1.default.join(dir, `${name}.figma.batch.json`),
396
+ });
397
+ const basePaths = getPaths(basename);
398
+ if ((fs_1.default.existsSync(basePaths.templatePath) && !filePathsCreated?.has(basePaths.templatePath)) ||
399
+ (fs_1.default.existsSync(basePaths.batchPath) && !filePathsCreated?.has(basePaths.batchPath))) {
400
+ return { skipped: true, reason: 'batch output file already exists' };
401
+ }
402
+ let paths = basePaths;
403
+ let counter = 1;
404
+ while (fs_1.default.existsSync(paths.templatePath) ||
405
+ fs_1.default.existsSync(paths.batchPath) ||
406
+ filePathsCreated?.has(paths.templatePath) ||
407
+ filePathsCreated?.has(paths.batchPath)) {
408
+ paths = getPaths(`${basename}_${counter}`);
409
+ counter++;
410
+ }
411
+ return { skipped: false, ...paths };
412
+ }
413
+ function writeBatchTemplateFiles(docs, outputDir, baseDir, { localSourcePath, filePathsCreated, includeProps = false, useTypeScript = true, } = {}) {
414
+ const basename = getSourceBasename(localSourcePath, docs);
415
+ const paths = determineBatchOutputPaths(basename, outputDir, baseDir, localSourcePath, useTypeScript, filePathsCreated);
416
+ if (paths.skipped) {
417
+ return { skipped: true, reason: paths.reason, componentCount: docs.length };
418
+ }
419
+ const built = buildBatchMigration(docs, {
420
+ includeProps,
421
+ useTypeScript,
422
+ templateFile: `./${path_1.default.basename(paths.templatePath)}`,
423
+ });
424
+ if (!built.ok) {
425
+ return { skipped: true, reason: built.reason, componentCount: docs.length };
426
+ }
427
+ fs_1.default.mkdirSync(path_1.default.dirname(paths.templatePath), { recursive: true });
428
+ fs_1.default.writeFileSync(paths.templatePath, built.template, 'utf-8');
429
+ fs_1.default.writeFileSync(paths.batchPath, `${JSON.stringify(built.batchJson, null, 2)}\n`, 'utf-8');
430
+ filePathsCreated?.add(paths.templatePath);
431
+ filePathsCreated?.add(paths.batchPath);
432
+ return {
433
+ skipped: false,
434
+ templatePath: paths.templatePath,
435
+ batchPath: paths.batchPath,
436
+ componentCount: docs.length,
437
+ };
438
+ }
439
+ //# sourceMappingURL=migration_batch_helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration_batch_helpers.js","sourceRoot":"","sources":["../../src/connect/migration_batch_helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { CodeConnectJSON } from './figma_connect'\nimport {\n formatTemplate,\n getFilenameFromComponentName,\n prepareMigratedTemplateBody,\n} from './migration_helpers'\n\nexport const AUTO_BATCH_THRESHOLD = 10\n\ntype BatchComponentEntry = {\n url: string\n source?: string\n component?: string\n [key: string]: any\n}\n\ntype Candidate = {\n doc: CodeConnectJSON\n body: string\n params: BatchComponentEntry\n}\n\ntype BatchMigrationBuildResult =\n | {\n ok: true\n template: string\n batchJson: {\n templateFile: string\n components: BatchComponentEntry[]\n }\n }\n | { ok: false; reason: string }\n\nexport type WriteBatchTemplateFileResult = {\n templatePath?: string\n batchPath?: string\n skipped: boolean\n reason?: string\n componentCount: number\n}\n\ntype WriteBatchTemplateFileOptions = {\n localSourcePath?: string\n filePathsCreated?: Set<string>\n includeProps?: boolean\n useTypeScript?: boolean\n}\n\ntype ImportInfo = {\n kind: 'named' | 'default'\n symbol: string\n importPath: string\n raw: string\n}\n\nfunction uniqueValues(values: Array<string | undefined>) {\n return new Set(values)\n}\n\nexport function getBatchMigrationGroups(\n docs: CodeConnectJSON[],\n {\n batchAll = false,\n disabled = false,\n threshold = AUTO_BATCH_THRESHOLD,\n }: {\n batchAll?: boolean\n disabled?: boolean\n threshold?: number\n } = {},\n) {\n if (disabled) {\n return new Map<string, CodeConnectJSON[]>()\n }\n\n const docsBySourcePath = new Map<string, CodeConnectJSON[]>()\n for (const doc of docs) {\n if (!doc._codeConnectFilePath) {\n continue\n }\n\n const sourcePath = path.resolve(doc._codeConnectFilePath)\n docsBySourcePath.set(sourcePath, [...(docsBySourcePath.get(sourcePath) || []), doc])\n }\n\n const batchGroups = new Map<string, CodeConnectJSON[]>()\n for (const [sourcePath, sourceDocs] of docsBySourcePath) {\n if (batchAll || sourceDocs.length >= threshold) {\n batchGroups.set(sourcePath, sourceDocs)\n }\n }\n\n return batchGroups\n}\n\nfunction allEqual(values: string[]) {\n return values.every((value) => value === values[0])\n}\n\nfunction isIdentifier(value: string | undefined): value is string {\n return !!value && /^[A-Za-z_$][\\w$]*$/.test(value)\n}\n\nfunction escapeRegExp(value: string) {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction applyReplacements(\n body: string,\n replacements: Array<{ start: number; end: number; value: string }>,\n) {\n return replacements\n .sort((a, b) => b.start - a.start)\n .reduce((result, replacement) => {\n return result.slice(0, replacement.start) + replacement.value + result.slice(replacement.end)\n }, body)\n}\n\nfunction removeUnusedSimpleConsts(body: string) {\n let previous: string\n let next = body\n const simpleConstRegex =\n /^const\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*(?:\"[^\"\\n]*\"|'[^'\\n]*'|figma\\.helpers\\.react\\.identifier\\([\"'][^\"'\\n]+[\"']\\)|[A-Za-z_$][\\w$]*|true|false|-?\\d+(?:\\.\\d+)?)\\s*\\n/gm\n\n do {\n previous = next\n next = next.replace(simpleConstRegex, (match, name: string, offset: number) => {\n const rest = next.slice(offset + match.length)\n const isReferenced = new RegExp(`\\\\b${escapeRegExp(name)}\\\\b`).test(rest)\n return isReferenced ? match : ''\n })\n } while (next !== previous)\n\n return next\n}\n\nfunction deriveMetadata(\n docs: CodeConnectJSON[],\n):\n | { ok: true; comments: string[]; entries: BatchComponentEntry[] }\n | { ok: false; reason: string } {\n const sources = docs.map((doc) => doc.source || undefined)\n const components = docs.map((doc) => doc.component || undefined)\n const entries: BatchComponentEntry[] = docs.map((doc) => ({ url: doc.figmaNode }))\n const comments: string[] = []\n\n const metadataFields = [\n { name: 'source' as const, values: sources },\n { name: 'component' as const, values: components },\n ]\n\n for (const field of metadataFields) {\n const hasAny = field.values.some(Boolean)\n const hasAll = field.values.every(Boolean)\n const unique = uniqueValues(field.values)\n\n if (hasAny && !hasAll) {\n return {\n ok: false,\n reason: `Cannot batch: some docs have ${field.name} metadata and others do not`,\n }\n }\n\n if (!hasAny) {\n continue\n }\n\n if (unique.size === 1) {\n comments.push(`// ${field.name}=${field.values[0]}`)\n } else {\n docs.forEach((doc, index) => {\n const value = doc[field.name]\n if (value) {\n entries[index][field.name] = value\n }\n })\n }\n }\n\n return { ok: true, comments, entries }\n}\n\nfunction parseImport(importString: string): ImportInfo | null {\n const named = importString.match(\n /^import\\s+\\{\\s*([A-Za-z_$][\\w$]*)\\s*\\}\\s+from\\s+[\"']([^\"']+)[\"']\\s*;?$/,\n )\n if (named) {\n return { kind: 'named', symbol: named[1], importPath: named[2], raw: importString }\n }\n\n const defaultImport = importString.match(\n /^import\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+[\"']([^\"']+)[\"']\\s*;?$/,\n )\n if (defaultImport) {\n return {\n kind: 'default',\n symbol: defaultImport[1],\n importPath: defaultImport[2],\n raw: importString,\n }\n }\n\n return null\n}\n\nfunction tryIdSwap(candidates: Candidate[]): Candidate[] {\n const matches = candidates.map((candidate) => candidate.body.match(/\\bid:\\s*(['\"])([^'\"\\\\]+)\\1/))\n if (matches.some((match) => !match)) {\n return candidates\n }\n\n const ids = matches.map((match) => match![2])\n if (uniqueValues(ids).size <= 1) {\n return candidates\n }\n\n return candidates.map((candidate, index) => ({\n ...candidate,\n body: candidate.body.replace(matches[index]![0], 'id: figma.batch.id'),\n params: { ...candidate.params, id: ids[index] },\n }))\n}\n\nfunction tryImportSwap(candidates: Candidate[]): Candidate[] {\n const importLists = candidates.map((candidate) => candidate.doc.templateData?.imports || [])\n if (importLists.some((imports) => imports.length !== 1)) {\n return candidates\n }\n\n const importStrings = importLists.map((imports) => imports[0])\n if (uniqueValues(importStrings).size <= 1) {\n return candidates\n }\n\n const parsed = importStrings.map(parseImport)\n if (parsed.some((info) => !info)) {\n return candidates\n }\n\n const imports = parsed as ImportInfo[]\n if (!allEqual(imports.map((info) => info.kind))) {\n return candidates\n }\n\n const symbolVaries = uniqueValues(imports.map((info) => info.symbol)).size > 1\n const pathVaries = uniqueValues(imports.map((info) => info.importPath)).size > 1\n if (!symbolVaries && !pathVaries) {\n return candidates\n }\n\n return candidates.map((candidate, index) => {\n const info = imports[index]\n const symbol = symbolVaries ? '${figma.batch.componentName}' : info.symbol\n const importPath = pathVaries ? '${figma.batch.importPath}' : info.importPath\n const replacement =\n info.kind === 'named'\n ? `\\`import { ${symbol} } from \"${importPath}\"\\``\n : `\\`import ${symbol} from \"${importPath}\"\\``\n const encodedImport = JSON.stringify(info.raw)\n\n if (!candidate.body.includes(encodedImport)) {\n return candidate\n }\n\n return {\n ...candidate,\n body: candidate.body.replace(encodedImport, replacement),\n params: {\n ...candidate.params,\n ...(symbolVaries ? { componentName: info.symbol } : {}),\n ...(pathVaries ? { importPath: info.importPath } : {}),\n },\n }\n })\n}\n\nfunction templateLiteralTextRanges(content: string, contentStart: number) {\n const ranges: Array<{ start: number; end: number; text: string }> = []\n let textStart = 0\n let i = 0\n\n while (i < content.length) {\n if (content[i] === '$' && content[i + 1] === '{') {\n if (textStart < i) {\n ranges.push({\n start: contentStart + textStart,\n end: contentStart + i,\n text: content.slice(textStart, i),\n })\n }\n\n i += 2\n let depth = 1\n while (i < content.length && depth > 0) {\n if (content[i] === '{') depth++\n if (content[i] === '}') depth--\n i++\n }\n textStart = i\n continue\n }\n\n i++\n }\n\n if (textStart < content.length) {\n ranges.push({\n start: contentStart + textStart,\n end: contentStart + content.length,\n text: content.slice(textStart),\n })\n }\n\n return ranges\n}\n\nfunction figmaCodeTextRanges(body: string) {\n const ranges: Array<{ start: number; end: number; text: string }> = []\n const regex = /figma\\.code`([\\s\\S]*?)`/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(body))) {\n const contentStart = match.index + 'figma.code`'.length\n ranges.push(...templateLiteralTextRanges(match[1], contentStart))\n }\n\n return ranges\n}\n\nfunction replaceIdentifierInFigmaCode(body: string, identifier: string, replacement: string) {\n const ranges = figmaCodeTextRanges(body)\n const identifierRegex = new RegExp(\n `(^|[^A-Za-z0-9_$])${escapeRegExp(identifier)}(?=$|[^A-Za-z0-9_$])`,\n 'g',\n )\n const replacements: Array<{ start: number; end: number; value: string }> = []\n\n for (const range of ranges) {\n let match: RegExpExecArray | null\n while ((match = identifierRegex.exec(range.text))) {\n const prefixLength = match[1].length\n const start = range.start + match.index + prefixLength\n replacements.push({\n start,\n end: start + identifier.length,\n value: replacement,\n })\n }\n }\n\n return applyReplacements(body, replacements)\n}\n\nfunction tryComponentSymbolSwap(candidates: Candidate[]): Candidate[] {\n let withNames = candidates\n\n if (withNames.some((candidate) => !candidate.params.componentName)) {\n const ids = withNames.map((candidate) => candidate.params.id)\n if (ids.every(isIdentifier) && uniqueValues(ids).size > 1) {\n withNames = withNames.map((candidate) => ({\n ...candidate,\n params: { ...candidate.params, componentName: candidate.params.id },\n }))\n }\n }\n\n if (withNames.some((candidate) => !candidate.params.componentName)) {\n return candidates\n }\n\n const names = withNames.map((candidate) => candidate.params.componentName as string)\n if (uniqueValues(names).size <= 1) {\n return candidates\n }\n\n return withNames.map((candidate, index) => ({\n ...candidate,\n body: replaceIdentifierInFigmaCode(\n candidate.body,\n names[index],\n '${figma.batch.componentName}',\n ),\n }))\n}\n\ntype ScalarMatch = {\n key: string\n operator: string\n value: string\n valueStart: number\n valueEnd: number\n}\n\nfunction collectNamedScalars(body: string, kind: 'string' | 'numberBoolean') {\n const ranges = figmaCodeTextRanges(body)\n const regex =\n kind === 'string'\n ? /([A-Za-z_$][\\w$]*)\\s*(=|:)\\s*(['\"])([^'\"\\\\\\n]+)\\3/g\n : /([A-Za-z_$][\\w$]*)\\s*(=|:)\\s*(?:\\{\\s*)?(true|false|-?\\d+(?:\\.\\d+)?)(?:\\s*\\})?(?=$|[^A-Za-z0-9_$.])/g\n const matches: ScalarMatch[] = []\n\n for (const range of ranges) {\n let match: RegExpExecArray | null\n while ((match = regex.exec(range.text))) {\n const value = kind === 'string' ? match[4] : match[3]\n const valueOffset = match[0].lastIndexOf(value)\n matches.push({\n key: match[1],\n operator: match[2],\n value,\n valueStart: range.start + match.index + valueOffset,\n valueEnd: range.start + match.index + valueOffset + value.length,\n })\n }\n }\n\n return matches\n}\n\nfunction parseScalarValue(value: string, kind: 'string' | 'numberBoolean') {\n if (kind === 'string') {\n return value\n }\n\n if (value === 'true') {\n return true\n }\n if (value === 'false') {\n return false\n }\n\n return Number(value)\n}\n\nfunction tryNamedScalarSwap(\n candidates: Candidate[],\n kind: 'string' | 'numberBoolean',\n): Candidate[] {\n const matchesByCandidate = candidates.map((candidate) =>\n collectNamedScalars(candidate.body, kind),\n )\n const matchCount = matchesByCandidate[0]?.length || 0\n if (matchCount === 0 || matchesByCandidate.some((matches) => matches.length !== matchCount)) {\n return candidates\n }\n\n const replacementsByCandidate = candidates.map(\n () => [] as Array<{ start: number; end: number; value: string }>,\n )\n const fieldNames = new Set<string>()\n const nextCandidates = candidates.map((candidate) => ({\n ...candidate,\n params: { ...candidate.params },\n }))\n\n for (let matchIndex = 0; matchIndex < matchCount; matchIndex++) {\n const matches = matchesByCandidate.map((matches) => matches[matchIndex])\n const first = matches[0]\n if (\n matches.some((match) => match.key !== first.key || match.operator !== first.operator) ||\n uniqueValues(matches.map((match) => match.value)).size <= 1\n ) {\n continue\n }\n\n if (fieldNames.has(first.key) || first.key in nextCandidates[0].params) {\n return candidates\n }\n\n fieldNames.add(first.key)\n matches.forEach((match, candidateIndex) => {\n replacementsByCandidate[candidateIndex].push({\n start: match.valueStart,\n end: match.valueEnd,\n value: `\\${figma.batch.${first.key}}`,\n })\n nextCandidates[candidateIndex].params[first.key] = parseScalarValue(match.value, kind)\n })\n }\n\n if (fieldNames.size === 0) {\n return candidates\n }\n\n return nextCandidates.map((candidate, index) => ({\n ...candidate,\n body: applyReplacements(candidate.body, replacementsByCandidate[index]),\n }))\n}\n\nexport function buildBatchMigration(\n docs: CodeConnectJSON[],\n {\n includeProps = false,\n useTypeScript = true,\n templateFile = './template.figma.batch.ts',\n }: { includeProps?: boolean; useTypeScript?: boolean; templateFile?: string } = {},\n): BatchMigrationBuildResult {\n if (docs.length < 2) {\n return { ok: false, reason: 'Cannot batch: at least two docs are required' }\n }\n\n if (docs.some((doc) => doc.variant && Object.keys(doc.variant).length > 0)) {\n return { ok: false, reason: 'Cannot batch: variant Code Connect docs are not supported yet' }\n }\n\n if (docs.some((doc) => !doc.template)) {\n return { ok: false, reason: 'Cannot batch: every doc must have a generated template' }\n }\n\n const metadata = deriveMetadata(docs)\n if (!metadata.ok) {\n return metadata\n }\n\n let candidates: Candidate[] = docs.map((doc, index) => ({\n doc,\n body: removeUnusedSimpleConsts(prepareMigratedTemplateBody(doc, includeProps, useTypeScript)),\n params: metadata.entries[index],\n }))\n\n candidates = tryIdSwap(candidates)\n candidates = tryImportSwap(candidates)\n candidates = tryComponentSymbolSwap(candidates)\n candidates = tryNamedScalarSwap(candidates, 'string')\n candidates = tryNamedScalarSwap(candidates, 'numberBoolean')\n\n if (!allEqual(candidates.map((candidate) => candidate.body))) {\n return {\n ok: false,\n reason: 'Cannot batch: templates did not reduce to one compatible shape',\n }\n }\n\n const templateBody = formatTemplate(candidates[0].body)\n const comments = metadata.comments.length > 0 ? `${metadata.comments.join('\\n')}\\n\\n` : ''\n\n return {\n ok: true,\n template: `${comments}${templateBody}`,\n batchJson: {\n templateFile,\n components: candidates.map((candidate) => candidate.params),\n },\n }\n}\n\nfunction getSourceBasename(localSourcePath: string | undefined, docs: CodeConnectJSON[]) {\n if (!localSourcePath) {\n return getFilenameFromComponentName(docs[0]?.component || 'batch')\n }\n\n const sourceBasename = path.basename(localSourcePath)\n const codeConnectPattern = /\\.(figma|figmadoc)(\\.[^.]+)+$/\n return codeConnectPattern.test(sourceBasename)\n ? sourceBasename.replace(codeConnectPattern, '')\n : path.basename(localSourcePath, path.extname(localSourcePath))\n}\n\nfunction determineBatchOutputPaths(\n basename: string,\n outputDir: string | undefined,\n baseDir: string,\n localSourcePath: string | undefined,\n useTypeScript: boolean,\n filePathsCreated: Set<string> | undefined,\n): { skipped: true; reason: string } | { skipped: false; templatePath: string; batchPath: string } {\n const dir = outputDir || (localSourcePath ? path.dirname(localSourcePath) : baseDir)\n const templateSuffix = useTypeScript ? '.figma.batch.ts' : '.figma.batch.js'\n const getPaths = (name: string) => ({\n templatePath: path.join(dir, `${name}${templateSuffix}`),\n batchPath: path.join(dir, `${name}.figma.batch.json`),\n })\n const basePaths = getPaths(basename)\n\n if (\n (fs.existsSync(basePaths.templatePath) && !filePathsCreated?.has(basePaths.templatePath)) ||\n (fs.existsSync(basePaths.batchPath) && !filePathsCreated?.has(basePaths.batchPath))\n ) {\n return { skipped: true, reason: 'batch output file already exists' }\n }\n\n let paths = basePaths\n let counter = 1\n while (\n fs.existsSync(paths.templatePath) ||\n fs.existsSync(paths.batchPath) ||\n filePathsCreated?.has(paths.templatePath) ||\n filePathsCreated?.has(paths.batchPath)\n ) {\n paths = getPaths(`${basename}_${counter}`)\n counter++\n }\n\n return { skipped: false, ...paths }\n}\n\nexport function writeBatchTemplateFiles(\n docs: CodeConnectJSON[],\n outputDir: string | undefined,\n baseDir: string,\n {\n localSourcePath,\n filePathsCreated,\n includeProps = false,\n useTypeScript = true,\n }: WriteBatchTemplateFileOptions = {},\n): WriteBatchTemplateFileResult {\n const basename = getSourceBasename(localSourcePath, docs)\n const paths = determineBatchOutputPaths(\n basename,\n outputDir,\n baseDir,\n localSourcePath,\n useTypeScript,\n filePathsCreated,\n )\n\n if (paths.skipped) {\n return { skipped: true, reason: paths.reason, componentCount: docs.length }\n }\n\n const built = buildBatchMigration(docs, {\n includeProps,\n useTypeScript,\n templateFile: `./${path.basename(paths.templatePath)}`,\n })\n\n if (!built.ok) {\n return { skipped: true, reason: built.reason, componentCount: docs.length }\n }\n\n fs.mkdirSync(path.dirname(paths.templatePath), { recursive: true })\n fs.writeFileSync(paths.templatePath, built.template, 'utf-8')\n fs.writeFileSync(paths.batchPath, `${JSON.stringify(built.batchJson, null, 2)}\\n`, 'utf-8')\n filePathsCreated?.add(paths.templatePath)\n filePathsCreated?.add(paths.batchPath)\n\n return {\n skipped: false,\n templatePath: paths.templatePath,\n batchPath: paths.batchPath,\n componentCount: docs.length,\n }\n}\n"]}
@@ -1,5 +1,9 @@
1
1
  import { CodeConnectJSON } from '../connect/figma_connect';
2
+ /** Formats template code with consistent prettier configuration */
3
+ export declare function formatTemplate(code: string): string;
2
4
  export declare function getFilenameFromComponentName(componentName: string): string;
5
+ /** Migrates a doc's template (Swift helpers, server helpers, V2, id, imports, nestable) without formatting. */
6
+ export declare function prepareMigratedTemplateBody(doc: CodeConnectJSON, includeProps?: boolean, useTypeScript?: boolean): string;
3
7
  /** Migrates a doc's template (Swift helpers, server helpers, V2, id, imports, nestable) and returns the formatted string. */
4
8
  export declare function prepareMigratedTemplate(doc: CodeConnectJSON, includeProps?: boolean, useTypeScript?: boolean): string;
5
9
  export type WriteTemplateFileOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"migration_helpers.d.ts","sourceRoot":"","sources":["../../src/connect/migration_helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAuC1D,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAQ1E;AAsGD,6HAA6H;AAC7H,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,eAAe,EACpB,YAAY,UAAQ,EACpB,aAAa,CAAC,EAAE,OAAO,GACtB,MAAM,CAeR;AAqCD,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,EACf,EACE,eAAe,EACf,gBAAgB,EAChB,YAAoB,EACpB,aAAoB,GACrB,GAAE,wBAA6B,GAC/B;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAyB1C;AAGD,wBAAgB,qCAAqC,CAAC,QAAQ,EAAE,MAAM,UAqBrE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAsBlF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAGjF;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,KAAG,MA8FxD,CAAA;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBzE;AAED,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,oBAAoB,eAAe,EAAE,kDAkBtF,CAAA;AAED,mIAAmI;AACnI,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,EACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,EACf,EACE,eAAe,EACf,gBAAgB,EAChB,aAAoB,EACpB,YAAoB,GACrB,GAAE,wBAA6B,GAC/B;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiG1C"}
1
+ {"version":3,"file":"migration_helpers.d.ts","sourceRoot":"","sources":["../../src/connect/migration_helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAU1D,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA0BD,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAQ1E;AAsGD,+GAA+G;AAC/G,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,eAAe,EACpB,YAAY,UAAQ,EACpB,aAAa,CAAC,EAAE,OAAO,GACtB,MAAM,CAeR;AAED,6HAA6H;AAC7H,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,eAAe,EACpB,YAAY,UAAQ,EACpB,aAAa,CAAC,EAAE,OAAO,GACtB,MAAM,CAGR;AAqCD,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,EACf,EACE,eAAe,EACf,gBAAgB,EAChB,YAAoB,EACpB,aAAoB,GACrB,GAAE,wBAA6B,GAC/B;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAyB1C;AAGD,wBAAgB,qCAAqC,CAAC,QAAQ,EAAE,MAAM,UAqBrE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAsBlF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAGjF;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,KAAG,MAgHxD,CAAA;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBzE;AAED,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,oBAAoB,eAAe,EAAE,kDAkBtF,CAAA;AAED,mIAAmI;AACnI,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,EACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,EACf,EACE,eAAe,EACf,gBAAgB,EAChB,aAAoB,EACpB,YAAoB,GACrB,GAAE,wBAA6B,GAC/B;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiG1C"}
@@ -37,7 +37,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.groupCodeConnectObjectsByFigmaUrl = exports.migrateV1TemplateToV2 = void 0;
40
+ exports.formatTemplate = formatTemplate;
40
41
  exports.getFilenameFromComponentName = getFilenameFromComponentName;
42
+ exports.prepareMigratedTemplateBody = prepareMigratedTemplateBody;
41
43
  exports.prepareMigratedTemplate = prepareMigratedTemplate;
42
44
  exports.writeTemplateFile = writeTemplateFile;
43
45
  exports.migrateTemplateToUseServerSideHelpers = migrateTemplateToUseServerSideHelpers;
@@ -158,8 +160,8 @@ function writeFileWithDuplicateHandling(baseOutputPath, fileContent, suffix, fil
158
160
  }
159
161
  return { outputPath, skipped: false };
160
162
  }
161
- /** Migrates a doc's template (Swift helpers, server helpers, V2, id, imports, nestable) and returns the formatted string. */
162
- function prepareMigratedTemplate(doc, includeProps = false, useTypeScript) {
163
+ /** Migrates a doc's template (Swift helpers, server helpers, V2, id, imports, nestable) without formatting. */
164
+ function prepareMigratedTemplateBody(doc, includeProps = false, useTypeScript) {
163
165
  let template = doc.template;
164
166
  template = removeSwiftHelpers(template);
165
167
  template = migrateTemplateToUseServerSideHelpers(template);
@@ -173,6 +175,11 @@ function prepareMigratedTemplate(doc, includeProps = false, useTypeScript) {
173
175
  if (useTypeScript) {
174
176
  template = convertSyntaxToTypeScript(template);
175
177
  }
178
+ return template;
179
+ }
180
+ /** Migrates a doc's template (Swift helpers, server helpers, V2, id, imports, nestable) and returns the formatted string. */
181
+ function prepareMigratedTemplate(doc, includeProps = false, useTypeScript) {
182
+ const template = prepareMigratedTemplateBody(doc, includeProps, useTypeScript);
176
183
  return formatTemplate(template);
177
184
  }
178
185
  function removeSwiftHelpers(template) {
@@ -301,9 +308,23 @@ const migrateV1TemplateToV2 = (template) => {
301
308
  // 5. Other method renames
302
309
  migrated = migrated.replace(/\.__getPropertyValue__\(/g, '.getPropertyValue(');
303
310
  // 6. __findChildWithCriteria__ - migrate based on type parameter
304
- // For TEXT type with __render__(): __findChildWithCriteria__({ name: 'X', type: "TEXT" }).__render__() → findText('X').textContent
305
- migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*name:\s*'([^']+)',\s*type:\s*"TEXT"\s*\}\)\.__render__\(\)/g, ".findText('$1').textContent");
306
- migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*type:\s*"TEXT",\s*name:\s*'([^']+)'\s*\}\)\.__render__\(\)/g, ".findText('$1').textContent");
311
+ // For TEXT type with __render__(): __findChildWithCriteria__({ name: 'X', type: "TEXT" }).__render__() → findText('X').__render__()
312
+ //
313
+ // We intentionally keep `.__render__()` here rather than the friendlier
314
+ // `.textContent`. `findText` returns `TextHandle | ErrorHandle`, and
315
+ // `textContent` only exists on `TextHandle` - so `.findText('X').textContent`
316
+ // (a) fails to typecheck (`textContent` does not exist on `ErrorHandle`), and
317
+ // (b) silently evaluates to `undefined` for a missing layer, swallowing the
318
+ // error. `__render__()` exists on both handles (returning the text string on
319
+ // success and an error section on failure), exactly matching the pre-migration
320
+ // runtime behavior of the React parser's `figma.textContent('X')`.
321
+ //
322
+ // We emit a sentinel so the blanket `__render__()` -> `executeTemplate().example`
323
+ // rewrite below (step 8) doesn't clobber it - `TextHandle` has no
324
+ // `executeTemplate`, so that rewrite would re-break the text case. The sentinel
325
+ // is converted back to `__render__()` at the end of this function.
326
+ migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*name:\s*'([^']+)',\s*type:\s*"TEXT"\s*\}\)\.__render__\(\)/g, ".findText('$1').__FCC_TEXT_RENDER__()");
327
+ migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*type:\s*"TEXT",\s*name:\s*'([^']+)'\s*\}\)\.__render__\(\)/g, ".findText('$1').__FCC_TEXT_RENDER__()");
307
328
  // For INSTANCE type: __findChildWithCriteria__({ type: 'INSTANCE', name: 'X' }) → findInstance('X')
308
329
  migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*name:\s*'([^']+)',\s*type:\s*['"]INSTANCE['"]\s*\}\)/g, ".findInstance('$1')");
309
330
  migrated = migrated.replace(/\.__findChildWithCriteria__\(\{\s*type:\s*['"]INSTANCE['"],\s*name:\s*'([^']+)'\s*\}\)/g, ".findInstance('$1')");
@@ -328,6 +349,9 @@ const migrateV1TemplateToV2 = (template) => {
328
349
  // 11. Export format - spread operator case
329
350
  // { ...figma.code`...`, metadata: ... } → { example: figma.code`...`, metadata: ... }
330
351
  migrated = migrated.replace(/\{\s*\.\.\.figma\.(code|tsx|html|swift|kotlin)`/g, '{ example: figma.$1`');
352
+ // 12. Restore the TEXT __render__ sentinel emitted in step 6 (kept out of the
353
+ // way of step 8's __render__ -> executeTemplate().example rewrite).
354
+ migrated = migrated.replace(/\.__FCC_TEXT_RENDER__\(\)/g, '.__render__()');
331
355
  return migrated;
332
356
  };
333
357
  exports.migrateV1TemplateToV2 = migrateV1TemplateToV2;