@figma/code-connect 1.4.1 → 1.4.3

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 (50) hide show
  1. package/README.md +4 -4
  2. package/dist/commands/connect.d.ts +6 -4
  3. package/dist/commands/connect.d.ts.map +1 -1
  4. package/dist/commands/connect.js +80 -107
  5. package/dist/commands/connect.js.map +1 -1
  6. package/dist/commands/filter_project_info.d.ts +7 -0
  7. package/dist/commands/filter_project_info.d.ts.map +1 -0
  8. package/dist/commands/filter_project_info.js +32 -0
  9. package/dist/commands/filter_project_info.js.map +1 -0
  10. package/dist/connect/helpers.d.ts +6 -0
  11. package/dist/connect/helpers.d.ts.map +1 -1
  12. package/dist/connect/helpers.js +18 -0
  13. package/dist/connect/helpers.js.map +1 -1
  14. package/dist/connect/intrinsics.d.ts +3 -3
  15. package/dist/connect/intrinsics.d.ts.map +1 -1
  16. package/dist/connect/intrinsics.js +21 -10
  17. package/dist/connect/intrinsics.js.map +1 -1
  18. package/dist/connect/migration_helpers.d.ts +15 -1
  19. package/dist/connect/migration_helpers.d.ts.map +1 -1
  20. package/dist/connect/migration_helpers.js +187 -73
  21. package/dist/connect/migration_helpers.js.map +1 -1
  22. package/dist/connect/parser_common.d.ts +3 -2
  23. package/dist/connect/parser_common.d.ts.map +1 -1
  24. package/dist/connect/parser_common.js +2 -2
  25. package/dist/connect/parser_common.js.map +1 -1
  26. package/dist/connect/project.d.ts +8 -3
  27. package/dist/connect/project.d.ts.map +1 -1
  28. package/dist/connect/project.js +25 -11
  29. package/dist/connect/project.js.map +1 -1
  30. package/dist/connect/raw_templates.d.ts +11 -0
  31. package/dist/connect/raw_templates.d.ts.map +1 -0
  32. package/dist/connect/raw_templates.js +149 -0
  33. package/dist/connect/raw_templates.js.map +1 -0
  34. package/dist/connect/upload.d.ts.map +1 -1
  35. package/dist/connect/upload.js +27 -6
  36. package/dist/connect/upload.js.map +1 -1
  37. package/dist/html/parser.d.ts +2 -2
  38. package/dist/html/parser.d.ts.map +1 -1
  39. package/dist/html/parser.js +9 -9
  40. package/dist/html/parser.js.map +1 -1
  41. package/dist/react/parser.d.ts +3 -3
  42. package/dist/react/parser.d.ts.map +1 -1
  43. package/dist/react/parser.js +14 -12
  44. package/dist/react/parser.js.map +1 -1
  45. package/dist/storybook/convert.d.ts +7 -1
  46. package/dist/storybook/convert.d.ts.map +1 -1
  47. package/dist/storybook/convert.js +12 -5
  48. package/dist/storybook/convert.js.map +1 -1
  49. package/figma-types.d.ts +214 -0
  50. package/package.json +7 -3
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Code Connect is a tool for connecting your design system components in code with your design system in Figma. When using Code Connect, Figma's Dev Mode will display true-to-production code snippets from your design system instead of autogenerated code examples. In addition to connecting component definitions, Code Connect also supports mapping properties from code to Figma enabling dynamic and correct examples. This can be useful for when you have an existing design system and are looking to drive consistent and correct adoption of that design system across design and engineering.
4
4
 
5
- Code Connect is easy to set up, easy to maintain, type-safe, and extensible. Out of the box Code Connect comes with support for React (and React Native), Storybook, HTML (e.g. Web Components, Angular and Vue), SwiftUI and Jetpack Compose.
5
+ Code Connect is easy to set up, easy to maintain, type-safe, and extensible. The easiest way to get started is using **template files** — a framework-agnostic way to represent any code snippet, giving you full control over how components appear in Dev Mode. Code Connect also includes framework-specific integrations for React (and React Native), Storybook, HTML (e.g. Web Components, Angular and Vue), SwiftUI and Jetpack Compose.
6
6
 
7
7
  ![image](https://static.figma.com/uploads/d98e747613e01685d6a0f9dd3e2dcd022ff289c0.png)
8
8
 
@@ -13,11 +13,11 @@ Code Connect is easy to set up, easy to maintain, type-safe, and extensible. Out
13
13
 
14
14
  Use Figma’s Code Connect to make your design system easily accessible to your developers and create a shared source of truth for both the design and code elements.
15
15
 
16
- Our Getting Started guide will walk you through the process of setting up Code Connect, using our interactive setup to automatically map components, and publishing your first components. **[Code Connect Documentation →](https://developers.figma.com/docs/code-connect/)**
16
+ Our Getting Started guide will walk you through setting up Code Connect and publishing your first components using template files. **[Code Connect Documentation →](https://developers.figma.com/docs/code-connect/)**
17
17
 
18
- Code Connect comes with support for different frameworks and languages. Our integration guides will walk you through the process of refining your components by mapping props and variants for:
18
+ For framework-specific prop mapping and variant support, see the integration guides for:
19
19
 
20
20
  - **[React (and React Native) →](https://www.figma.com/code-connect-docs/react)**
21
21
  - **[HTML (e.g. Web Components, Angular and Vue) →](https://www.figma.com/code-connect-docs/html)**
22
22
  - **[SwiftUI →](https://www.figma.com/code-connect-docs/swiftui)**
23
- - **[Jetpack Compose →](https://www.figma.com/code-connect-docs/compose)**.
23
+ - **[Jetpack Compose →](https://www.figma.com/code-connect-docs/compose)**
@@ -1,5 +1,5 @@
1
1
  import * as commander from 'commander';
2
- import { CodeConnectConfig, ProjectInfo } from '../connect/project';
2
+ import { ProjectInfo } from '../connect/project';
3
3
  import { CodeConnectJSON } from '../connect/figma_connect';
4
4
  export type BaseCommand = commander.Command & {
5
5
  token: string;
@@ -9,17 +9,19 @@ export type BaseCommand = commander.Command & {
9
9
  config: string;
10
10
  dryRun: boolean;
11
11
  dir: string;
12
+ file: string;
12
13
  jsonFile: string;
13
14
  skipUpdateCheck: boolean;
14
15
  exitOnUnreadableFiles: boolean;
15
16
  apiUrl?: string;
16
- removeProps?: boolean;
17
+ includeProps?: boolean;
17
18
  };
18
19
  export declare function addConnectCommandToProgram(program: commander.Command): void;
19
20
  export declare function getAccessToken(cmd: BaseCommand): string;
21
+ export declare function getAccessTokenOrExit(cmd: BaseCommand): string;
20
22
  export declare function getDir(cmd: BaseCommand): string;
21
- export declare function parseRawFile(filePath: string, label: string | undefined, config?: CodeConnectConfig, dir?: string): CodeConnectJSON;
23
+ export declare function setupHandler(cmd: BaseCommand): void;
22
24
  export declare function getCodeConnectObjects(cmd: BaseCommand & {
23
25
  label?: string;
24
- }, projectInfo: ProjectInfo, silent?: boolean, skipTemplateHelpers?: boolean): Promise<CodeConnectJSON[]>;
26
+ }, projectInfo: ProjectInfo, silent?: boolean, isForMigration?: boolean): Promise<CodeConnectJSON[]>;
25
27
  //# sourceMappingURL=connect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AAKtC,OAAO,EACL,iBAAiB,EAIjB,WAAW,EAKZ,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AA0B1D,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,OAAO,CAAA;IACxB,qBAAqB,EAAE,OAAO,CAAA;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AA0BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,QAiEpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,UAE9C;AAcD,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,UAEtC;AA0CD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,GAAG,CAAC,EAAE,MAAM,GACX,eAAe,CAoEjB;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EACrC,WAAW,EAAE,WAAW,EACxB,MAAM,UAAQ,EACd,mBAAmB,UAAQ,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAkH5B"}
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AAKtC,OAAO,EAKL,WAAW,EAKZ,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAgC1D,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,OAAO,CAAA;IACxB,qBAAqB,EAAE,OAAO,CAAA;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AA2BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,QA4EpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,UAE9C;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,UAUpD;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,UAEtC;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,QAI5C;AAkCD,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EACrC,WAAW,EAAE,WAAW,EACxB,MAAM,UAAQ,EACd,cAAc,UAAQ,GACrB,OAAO,CAAC,eAAe,EAAE,CAAC,CA6H5B"}
@@ -5,8 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.addConnectCommandToProgram = addConnectCommandToProgram;
7
7
  exports.getAccessToken = getAccessToken;
8
+ exports.getAccessTokenOrExit = getAccessTokenOrExit;
8
9
  exports.getDir = getDir;
9
- exports.parseRawFile = parseRawFile;
10
+ exports.setupHandler = setupHandler;
10
11
  exports.getCodeConnectObjects = getCodeConnectObjects;
11
12
  const fs_1 = __importDefault(require("fs"));
12
13
  const upload_1 = require("../connect/upload");
@@ -23,11 +24,13 @@ const parser_executable_types_1 = require("../connect/parser_executable_types");
23
24
  const path_1 = __importDefault(require("path"));
24
25
  const updates_1 = require("../common/updates");
25
26
  const helpers_1 = require("../connect/helpers");
27
+ const filter_project_info_1 = require("./filter_project_info");
26
28
  const parser_1 = require("../html/parser");
27
29
  const parser_common_1 = require("../connect/parser_common");
28
30
  const parser_2 = require("../react/parser");
29
31
  const label_language_mapping_1 = require("../connect/label_language_mapping");
30
32
  const migration_helpers_1 = require("../connect/migration_helpers");
33
+ const raw_templates_1 = require("../connect/raw_templates");
31
34
  const run_wizard_2 = require("../connect/wizard/run_wizard");
32
35
  const project_2 = require("../connect/project");
33
36
  function addBaseCommand(command, name, description) {
@@ -36,6 +39,7 @@ function addBaseCommand(command, name, description) {
36
39
  .description(description)
37
40
  .usage('[options]')
38
41
  .option('-r --dir <dir>', 'directory to parse')
42
+ .option('-f --file <file>', 'path to a single Code Connect file to process')
39
43
  .option('-t --token <token>', 'figma access token')
40
44
  .option('-v --verbose', 'enable verbose logging for debugging')
41
45
  .option('-o --outFile <file>', 'specify a file to output generated Code Connect')
@@ -57,6 +61,7 @@ function addConnectCommandToProgram(program) {
57
61
  .option('--skip-validation', 'skip validation of Code Connect docs')
58
62
  .option('-l --label <label>', 'label to apply to the published files')
59
63
  .option('-b --batch-size <batch_size>', 'optional batch size (in number of documents) to use when uploading. Use this if you hit "request too large" errors. See README for more information.')
64
+ .option('--include-template-files', '(Deprecated) No longer needed - template files are included by default. Will be removed in a future version.')
60
65
  .action((0, updates_1.withUpdateCheck)(handlePublish));
61
66
  addBaseCommand(connectCommand, 'unpublish', 'Run to find any files that have figma connections and unpublish them from Figma. ' +
62
67
  'By default this looks for a config file named "figma.config.json", and uses the `include` and `exclude` fields to determine which files to parse. ' +
@@ -66,15 +71,15 @@ function addConnectCommandToProgram(program) {
66
71
  .action((0, updates_1.withUpdateCheck)(handleUnpublish));
67
72
  addBaseCommand(connectCommand, 'parse', 'Run Code Connect locally to find any files that have figma connections, then converts them to JSON and outputs to stdout.')
68
73
  .option('-l --label <label>', 'label to apply to the parsed files')
74
+ .option('--include-template-files', '(Deprecated) No longer needed - template files are included by default. Will be removed in a future version.')
69
75
  .action((0, updates_1.withUpdateCheck)(handleParse));
70
76
  addBaseCommand(connectCommand, 'create', 'Generate a Code Connect file with boilerplate in the current directory for a Figma node URL')
71
77
  .argument('<figma-node-url>', 'Figma node URL to create the Code Connect file from')
72
78
  .action((0, updates_1.withUpdateCheck)(handleCreate));
73
79
  addBaseCommand(connectCommand, 'migrate', 'Parse Code Connect files and migrate their templates into .figma.js files that can be published directly without parsing.')
74
80
  .argument('[pattern]', 'glob pattern to match files (optional, uses config if not provided)')
75
- .option('-l --label <label>', 'label to apply to the migrated templates')
76
- .option('--output-dir <dir>', 'directory to write template files (default: same directory as source file)')
77
- .option('--remove-props', 'remove __props metadata blocks from migrated templates')
81
+ .option('--include-props', 'preserve __props metadata blocks in migrated templates (removed by default). Use if your templates use the React .getProps() or .render() modifiers, or read executeTemplate().metadata.__props from the migrated components')
82
+ .option('--javascript', 'output migrated templates as JavaScript (.figma.js) instead of TypeScript (.figma.ts)')
78
83
  .action((0, updates_1.withUpdateCheck)(handleMigrate));
79
84
  }
80
85
  function getAccessToken(cmd) {
@@ -105,15 +110,13 @@ function transformDocFromParser(doc, remoteUrl, config) {
105
110
  }
106
111
  }
107
112
  catch (e) {
108
- source = (0, project_1.getRemoteFileUrl)(source, remoteUrl);
113
+ source = (0, project_1.getRemoteFileUrl)(source, remoteUrl, config.defaultBranch);
109
114
  }
110
115
  }
111
116
  // TODO This logic is duplicated in parser.ts parseDoc due to some type issues
112
117
  let figmaNode = doc.figmaNode;
113
118
  if (config.documentUrlSubstitutions) {
114
- Object.entries(config.documentUrlSubstitutions).forEach(([from, to]) => {
115
- figmaNode = figmaNode.replace(from, to);
116
- });
119
+ figmaNode = (0, helpers_1.applyDocumentUrlSubstitutions)(figmaNode, config.documentUrlSubstitutions);
117
120
  }
118
121
  return {
119
122
  ...doc,
@@ -121,67 +124,7 @@ function transformDocFromParser(doc, remoteUrl, config) {
121
124
  figmaNode,
122
125
  };
123
126
  }
124
- function parseRawFile(filePath, label, config, dir) {
125
- const fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
126
- const lines = fileContent.split('\n');
127
- const fields = {};
128
- let templateStartIndex = 0;
129
- // Parse consecutive comment lines at the start of the file
130
- for (let i = 0; i < lines.length; i++) {
131
- const line = lines[i].trim();
132
- // Match pattern: // fieldName=value
133
- const match = line.match(/^\/\/\s*(\w+)=(.+)$/);
134
- if (match) {
135
- const [, fieldName, fieldValue] = match;
136
- const normalizedFieldName = fieldName.toLowerCase();
137
- if (normalizedFieldName === 'url' ||
138
- normalizedFieldName === 'component' ||
139
- normalizedFieldName === 'source') {
140
- fields[normalizedFieldName] = fieldValue.trim();
141
- }
142
- templateStartIndex = i + 1;
143
- }
144
- else if (line === '' || line.startsWith('//')) {
145
- // Allow blank lines or other comments, but don't increment template start
146
- continue;
147
- }
148
- else {
149
- // First non-comment line found
150
- break;
151
- }
152
- }
153
- if (!fields.url) {
154
- throw new Error(`Missing required url field in ${filePath}. Please add a // url=... comment to the top of the file.`);
155
- }
156
- let figmaNodeUrl = fields.url;
157
- const template = lines.slice(templateStartIndex).join('\n');
158
- // Apply documentUrlSubstitutions if provided
159
- if (config?.documentUrlSubstitutions) {
160
- Object.entries(config.documentUrlSubstitutions).forEach(([from, to]) => {
161
- figmaNodeUrl = figmaNodeUrl.replace(from, to);
162
- });
163
- }
164
- // Determine effective label from parameter, config, or default
165
- const effectiveLabel = label || config?.label || label_language_mapping_1.CodeConnectLabel.Code;
166
- const language = config?.language || (0, label_language_mapping_1.getInferredLanguageForRaw)(effectiveLabel);
167
- return {
168
- figmaNode: figmaNodeUrl,
169
- component: fields.component,
170
- template,
171
- // nestable by default unless user specifies in template
172
- // (templateData.nestable AND template.metadata.nestable need to
173
- // be true for instance to be nested)
174
- templateData: { nestable: true, isParserless: true },
175
- language,
176
- label: effectiveLabel,
177
- source: fields.source || '',
178
- sourceLocation: { line: -1 },
179
- metadata: {
180
- cliVersion: require('../../package.json').version,
181
- },
182
- };
183
- }
184
- async function getCodeConnectObjects(cmd, projectInfo, silent = false, skipTemplateHelpers = false) {
127
+ async function getCodeConnectObjects(cmd, projectInfo, silent = false, isForMigration = false) {
185
128
  if (cmd.jsonFile) {
186
129
  try {
187
130
  const docsFromJson = JSON.parse(fs_1.default.readFileSync(cmd.jsonFile, 'utf8'));
@@ -197,7 +140,7 @@ async function getCodeConnectObjects(cmd, projectInfo, silent = false, skipTempl
197
140
  }
198
141
  let codeConnectObjects = [];
199
142
  if (projectInfo.config.parser === 'react') {
200
- codeConnectObjects = await getReactCodeConnectObjects(projectInfo, cmd, silent, skipTemplateHelpers);
143
+ codeConnectObjects = await getReactCodeConnectObjects(projectInfo, cmd, silent, isForMigration);
201
144
  }
202
145
  else if (projectInfo.config.parser === 'html') {
203
146
  codeConnectObjects = await getCodeConnectObjectsFromParseFn({
@@ -206,14 +149,14 @@ async function getCodeConnectObjects(cmd, projectInfo, silent = false, skipTempl
206
149
  projectInfo,
207
150
  cmd,
208
151
  silent,
209
- skipTemplateHelpers,
152
+ isForMigration,
210
153
  });
211
154
  }
212
155
  else {
213
156
  const payload = {
214
157
  mode: 'PARSE',
215
158
  paths: projectInfo.files,
216
- config: { ...projectInfo.config, skipTemplateHelpers },
159
+ config: { ...projectInfo.config, skipTemplateHelpers: isForMigration },
217
160
  verbose: cmd.verbose,
218
161
  };
219
162
  try {
@@ -244,12 +187,26 @@ async function getCodeConnectObjects(cmd, projectInfo, silent = false, skipTempl
244
187
  }
245
188
  }
246
189
  }
247
- const rawTemplateFiles = projectInfo.files.filter((f) => f.endsWith('.figma.js') || f.endsWith('.figma.template.js'));
190
+ const rawTemplateFiles = projectInfo.files.filter((f) => {
191
+ // Suffix may be used by HTML / custom parsers
192
+ const isPotentialRawTemplate = f.endsWith('.figma.js') ||
193
+ f.endsWith('.figma.template.js') ||
194
+ f.endsWith('.figma.ts') ||
195
+ f.endsWith('.figma.template.ts');
196
+ if (!isPotentialRawTemplate)
197
+ return false;
198
+ try {
199
+ return (0, raw_templates_1.isRawTemplate)(fs_1.default.readFileSync(f, 'utf-8'));
200
+ }
201
+ catch {
202
+ return false;
203
+ }
204
+ });
248
205
  if (rawTemplateFiles.length > 0) {
249
206
  // Resolve the label before parsing so language inference works correctly
250
207
  const resolvedLabel = cmd.label || projectInfo.config.label;
251
208
  const rawTemplateDocs = rawTemplateFiles.map((file) => {
252
- const doc = parseRawFile(file, resolvedLabel, projectInfo.config, projectInfo.absPath);
209
+ const doc = (0, raw_templates_1.parseRawFile)(file, resolvedLabel, projectInfo.config, projectInfo.absPath);
253
210
  // Store the Code Connect file path for migration purposes
254
211
  doc._codeConnectFilePath = file;
255
212
  if (!silent || cmd.verbose) {
@@ -289,8 +246,8 @@ projectInfo,
289
246
  cmd,
290
247
  /** Silences console output */
291
248
  silent = false,
292
- /** Skip template helpers (used for extract command) */
293
- skipTemplateHelpers = false, }) {
249
+ /** Whether this parse is for the migrate command */
250
+ isForMigration = false, }) {
294
251
  const codeConnectObjects = [];
295
252
  const tsProgram = (0, project_1.getTsProgram)(projectInfo);
296
253
  const { files, remoteUrl, config, absPath } = projectInfo;
@@ -307,7 +264,7 @@ skipTemplateHelpers = false, }) {
307
264
  repoUrl: remoteUrl,
308
265
  debug: cmd.verbose,
309
266
  silent,
310
- skipTemplateHelpers,
267
+ isForMigration,
311
268
  },
312
269
  });
313
270
  codeConnectObjects.push(...docs);
@@ -343,7 +300,7 @@ skipTemplateHelpers = false, }) {
343
300
  }
344
301
  return codeConnectObjects;
345
302
  }
346
- async function getReactCodeConnectObjects(projectInfo, cmd, silent = false, skipTemplateHelpers = false) {
303
+ async function getReactCodeConnectObjects(projectInfo, cmd, silent = false, isForMigration = false) {
347
304
  const codeConnectObjects = await getCodeConnectObjectsFromParseFn({
348
305
  parseFn: parser_2.parseReactDoc,
349
306
  resolveImportsFn: parser_2.findAndResolveImports,
@@ -351,18 +308,24 @@ async function getReactCodeConnectObjects(projectInfo, cmd, silent = false, skip
351
308
  projectInfo,
352
309
  cmd,
353
310
  silent,
354
- skipTemplateHelpers,
311
+ isForMigration,
355
312
  });
356
313
  const storybookCodeConnectObjects = await (0, convert_1.convertStorybookFiles)({
357
314
  projectInfo: (0, project_1.getReactProjectInfo)(projectInfo),
315
+ isForMigration,
358
316
  });
359
317
  const allCodeConnectObjects = codeConnectObjects.concat(storybookCodeConnectObjects);
360
318
  return allCodeConnectObjects;
361
319
  }
362
320
  async function handlePublish(cmd) {
363
321
  setupHandler(cmd);
322
+ // Show deprecation warning if the flag is used
323
+ if (cmd.includeTemplateFiles !== undefined) {
324
+ logging_1.logger.warn('[Deprecated] The --include-template-files flag is no longer needed because template files are now included by default. ' +
325
+ "Please don't use this flag - it will be removed in a future version.");
326
+ }
364
327
  let dir = getDir(cmd);
365
- const projectInfo = await (0, project_1.getProjectInfo)(dir, cmd.config);
328
+ const projectInfo = (0, filter_project_info_1.filterProjectInfoByFile)(await (0, project_1.getProjectInfo)(dir, cmd.config), cmd.file);
366
329
  const codeConnectObjects = await getCodeConnectObjects(cmd, projectInfo);
367
330
  if (codeConnectObjects.length === 0) {
368
331
  logging_1.logger.warn(`No Code Connect files found in ${dir} - Make sure you have configured \`include\` and \`exclude\` in your figma.config.json file correctly, or that you are running in a directory that contains Code Connect files.`);
@@ -416,7 +379,7 @@ async function handleUnpublish(cmd) {
416
379
  logging_1.logger.info(`Files that would be unpublished:`);
417
380
  }
418
381
  let nodesToDeleteRelevantInfo;
419
- const projectInfo = await (0, project_1.getProjectInfo)(dir, cmd.config);
382
+ const projectInfo = (0, filter_project_info_1.filterProjectInfoByFile)(await (0, project_1.getProjectInfo)(dir, cmd.config), cmd.file);
420
383
  if (cmd.node) {
421
384
  if (!cmd.label) {
422
385
  (0, logging_1.exitWithError)('Label is required when specifying a node');
@@ -446,8 +409,13 @@ async function handleUnpublish(cmd) {
446
409
  }
447
410
  async function handleParse(cmd) {
448
411
  setupHandler(cmd);
412
+ // Show deprecation warning if the flag is used
413
+ if (cmd.includeTemplateFiles !== undefined) {
414
+ logging_1.logger.warn('[Deprecated] The --include-template-files flag is no longer needed because template files are now included by default. ' +
415
+ "Please don't use this flag - it will be removed in a future version.");
416
+ }
449
417
  const dir = cmd.dir ?? process.cwd();
450
- const projectInfo = await (0, project_1.getProjectInfo)(dir, cmd.config);
418
+ const projectInfo = (0, filter_project_info_1.filterProjectInfoByFile)(await (0, project_1.getProjectInfo)(dir, cmd.config), cmd.file);
451
419
  const codeConnectObjects = await getCodeConnectObjects(cmd, projectInfo);
452
420
  if (cmd.dryRun) {
453
421
  logging_1.logger.info(`Dry run complete`);
@@ -522,59 +490,67 @@ async function handleMigrate(pattern, cmd) {
522
490
  // Parse the files to get Code Connect objects
523
491
  const allCodeConnectObjects = await getCodeConnectObjects(cmd, projectInfo, false, true);
524
492
  const codeConnectObjectsByFigmaUrl = (0, migration_helpers_1.groupCodeConnectObjectsByFigmaUrl)(allCodeConnectObjects);
525
- const totalVariants = Object.values(codeConnectObjectsByFigmaUrl).reduce((acc, obj) => acc + obj.variants.length, 0);
526
- // TODO support variants (for now filtering on non-variants)
527
- const codeConnectObjects = Object.values(codeConnectObjectsByFigmaUrl).flatMap((obj) => obj.main ? [obj.main] : []);
528
- if (codeConnectObjects.length === 0) {
493
+ const groupCount = Object.keys(codeConnectObjectsByFigmaUrl).length;
494
+ if (groupCount === 0) {
529
495
  (0, logging_1.exitWithError)('No Code Connect objects found to migrate');
530
496
  }
531
- logging_1.logger.info(`Found ${codeConnectObjects.length} Code Connect object(s) to migrate`);
497
+ logging_1.logger.info(`Found ${groupCount} component(s) to migrate`);
532
498
  let migratedCount = 0;
533
499
  let skippedCount = 0;
534
500
  const errors = [];
535
- // Track file paths created during this migration run
536
501
  const filePathsCreated = new Set();
537
- // Get git root path for converting remote URLs to local paths
538
502
  const gitRootPath = (0, project_2.getGitRepoAbsolutePath)(dir);
539
- // Migrate each Code Connect object to a template file
540
- for (const doc of codeConnectObjects) {
503
+ const useTypeScript = !cmd.javascript;
504
+ for (const [figmaUrl, group] of Object.entries(codeConnectObjectsByFigmaUrl)) {
541
505
  try {
542
- // Skip if no template (shouldn't happen, but be safe)
543
- if (!doc.template) {
544
- logging_1.logger.warn(`Skipping ${doc.figmaNode}: no template found`);
506
+ const hasVariants = group.variants.length > 0;
507
+ const representativeDoc = group.main ?? group.variants[0];
508
+ if (!representativeDoc?.template) {
509
+ logging_1.logger.warn(`Skipping ${figmaUrl}: no template found`);
545
510
  skippedCount++;
546
511
  continue;
547
512
  }
548
513
  // Determine local source path for output location
549
514
  let localSourcePath;
550
- if (doc._codeConnectFilePath) {
515
+ if (representativeDoc._codeConnectFilePath) {
551
516
  // Use the Code Connect file path directly (preferred)
552
- localSourcePath = doc._codeConnectFilePath;
517
+ localSourcePath = representativeDoc._codeConnectFilePath;
553
518
  }
554
- else if (doc.source && gitRootPath) {
519
+ else if (representativeDoc?.source && gitRootPath) {
555
520
  // Fallback: Convert remote file URL to local path
556
521
  const relativePath = (0, run_wizard_2.convertRemoteFileUrlToRelativePath)({
557
- remoteFileUrl: doc.source,
522
+ remoteFileUrl: representativeDoc.source,
558
523
  gitRootPath,
559
524
  dir,
560
525
  });
561
526
  if (relativePath) {
562
- // Convert relative path to absolute path for writeTemplateFile
563
527
  localSourcePath = path_1.default.resolve(dir, relativePath);
564
528
  }
565
529
  }
566
- const { outputPath, skipped } = (0, migration_helpers_1.writeTemplateFile)(doc, cmd.outDir, dir, localSourcePath, filePathsCreated, cmd.removeProps);
530
+ const { outputPath, skipped } = hasVariants
531
+ ? (0, migration_helpers_1.writeVariantTemplateFile)(group, figmaUrl, cmd.outDir, dir, {
532
+ localSourcePath,
533
+ filePathsCreated,
534
+ useTypeScript,
535
+ includeProps: cmd.includeProps,
536
+ })
537
+ : (0, migration_helpers_1.writeTemplateFile)(representativeDoc, cmd.outDir, dir, {
538
+ localSourcePath,
539
+ filePathsCreated,
540
+ includeProps: cmd.includeProps,
541
+ useTypeScript,
542
+ });
567
543
  if (skipped) {
568
544
  logging_1.logger.warn(`Skipping ${outputPath}: file already exists`);
569
545
  skippedCount++;
570
546
  }
571
547
  else {
572
- logging_1.logger.info(`${(0, logging_1.success)('✓')} Migrated to ${(0, logging_1.highlight)(outputPath)}`);
548
+ logging_1.logger.info(`${(0, logging_1.success)('✓')} Migrated${hasVariants ? ' (with variants)' : ''} to ${(0, logging_1.highlight)(outputPath)}`);
573
549
  migratedCount++;
574
550
  }
575
551
  }
576
552
  catch (error) {
577
- const errorMsg = `Failed to migrate ${doc.figmaNode}: ${error}`;
553
+ const errorMsg = `Failed to migrate ${figmaUrl}: ${error}`;
578
554
  logging_1.logger.error(errorMsg);
579
555
  errors.push(errorMsg);
580
556
  }
@@ -586,10 +562,10 @@ async function handleMigrate(pattern, cmd) {
586
562
  logging_1.logger.warn(`Config file already exists at ${(0, logging_1.highlight)(configFilePath)}`);
587
563
  }
588
564
  else {
589
- const { language: docLanguage, label } = codeConnectObjects[0];
565
+ const { language: docLanguage, label } = allCodeConnectObjects[0];
590
566
  const language = (0, label_language_mapping_1.getInferredLanguageForRaw)(label, docLanguage);
591
567
  const config = {
592
- include: ['**/*.figma.js'],
568
+ include: [useTypeScript ? '**/*.figma.ts' : '**/*.figma.js'],
593
569
  language,
594
570
  label,
595
571
  ...(documentUrlSubstitutions && Object.keys(documentUrlSubstitutions).length > 0
@@ -604,9 +580,6 @@ async function handleMigrate(pattern, cmd) {
604
580
  // Summary
605
581
  console.log('');
606
582
  logging_1.logger.info(`Migration complete: ${(0, logging_1.success)(`${migratedCount} migrated`)}, ${skippedCount} skipped`);
607
- if (totalVariants > 0) {
608
- logging_1.logger.error(`${(0, logging_1.error)(`${totalVariants} variant restriction(s)`)} found, but not supported by the migration script yet`);
609
- }
610
583
  if (errors.length > 0) {
611
584
  console.log('');
612
585
  logging_1.logger.error(`Encountered ${errors.length} error(s):`);