@figma/code-connect 1.5.1 → 1.5.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"raw_templates.d.ts","sourceRoot":"","sources":["../../src/connect/raw_templates.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAY7C;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAatD;AA2JD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,GAAG,CAAC,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,eAAe,CAAC,CA+G1B"}
1
+ {"version":3,"file":"raw_templates.d.ts","sourceRoot":"","sources":["../../src/connect/raw_templates.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAY7C;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAatD;AA6JD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,GAAG,CAAC,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,eAAe,CAAC,CA+G1B"}
@@ -46,8 +46,9 @@ function isRawTemplate(content) {
46
46
  }
47
47
  return false;
48
48
  }
49
- // Convert ESM import of 'figma' to require syntax. Supports: import figma from 'figma'
50
- const figmaImportRegex = /^import\s+figma\s+from\s+['"]figma['"]\s*;?\s*$/m;
49
+ // Convert the supported ESM Figma import to the runtime's require syntax when
50
+ // the template does not otherwise need bundling.
51
+ const figmaImportRegex = /^import[ \t]+figma[ \t]+from[ \t]+['"]figma['"][ \t]*;?[ \t]*(?:\/\/[^\r\n]*)?$/m;
51
52
  // Matches the backend's max template size; we fail here rather than let the
52
53
  // server reject the publish request.
53
54
  const MAX_TEMPLATE_SIZE_MB = 1;
@@ -66,9 +67,9 @@ function isRawTemplateSourceFile(filePath) {
66
67
  /**
67
68
  * Validates a template entry's imports: only the default `figma` import,
68
69
  * type-only imports and relative helper imports are allowed. Returns whether
69
- * the entry imports helpers, so the caller knows whether to bundle.
70
+ * the entry imports helpers, so the caller knows whether to bundle it.
70
71
  */
71
- function validateTypeScriptTemplateImports(filePath, fileContent) {
72
+ function validateTemplateImports(filePath, fileContent) {
72
73
  const sourceFile = typescript_1.default.createSourceFile(filePath, fileContent, typescript_1.default.ScriptTarget.Latest, true);
73
74
  let hasRelativeHelperImports = false;
74
75
  for (const statement of sourceFile.statements) {
@@ -118,10 +119,10 @@ function validateTypeScriptTemplateImports(filePath, fileContent) {
118
119
  visitRequire(sourceFile);
119
120
  return { hasRelativeHelperImports };
120
121
  }
121
- function transpileTypeScriptTemplate(filePath, fileContent) {
122
- if (figmaImportRegex.test(fileContent)) {
123
- fileContent = fileContent.replace(figmaImportRegex, "const figma = require('figma')");
124
- }
122
+ function rewriteFigmaImport(fileContent) {
123
+ return fileContent.replace(figmaImportRegex, "const figma = require('figma')");
124
+ }
125
+ function transpileTypeScriptTemplate(fileContent) {
125
126
  const result = typescript_1.default.transpileModule(fileContent, {
126
127
  compilerOptions: {
127
128
  module: typescript_1.default.ModuleKind.ESNext,
@@ -168,7 +169,7 @@ function extractMetadataFields(fileContent) {
168
169
  }
169
170
  async function parseRawFile(filePath, label, config, dir, batchOverrides) {
170
171
  let fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
171
- let shouldBundleTypeScriptTemplate = false;
172
+ let shouldBundleTemplate = false;
172
173
  // Extract metadata fields BEFORE transpilation to avoid losing comments
173
174
  // that appear before type-only imports (which TypeScript erases)
174
175
  const { fields, templateStartLine } = extractMetadataFields(fileContent);
@@ -176,7 +177,7 @@ async function parseRawFile(filePath, label, config, dir, batchOverrides) {
176
177
  // Validate imports first, before the `codeProperties` skip guard below, so an
177
178
  // unsupported import is always a hard error rather than silently swallowed.
178
179
  if (isRawTemplateSourceFile(filePath)) {
179
- shouldBundleTypeScriptTemplate = validateTypeScriptTemplateImports(filePath, fileContent).hasRelativeHelperImports;
180
+ shouldBundleTemplate = validateTemplateImports(filePath, fileContent).hasRelativeHelperImports;
180
181
  }
181
182
  // A file with no // url= directive that contains the string `codeProperties`
182
183
  // is not Code Connect (e.g. Make's code component property definitions). Skip
@@ -185,15 +186,18 @@ async function parseRawFile(filePath, label, config, dir, batchOverrides) {
185
186
  if (!figmaUrl && fileContent.includes('codeProperties')) {
186
187
  throw new CodePropertiesError(`Skipping ${filePath}: file has no // url= directive and contains "codeProperties", so it is not treated as a Code Connect file.`);
187
188
  }
188
- // Bundle when helpers are present; otherwise transpile TS and emit JS verbatim.
189
- // Bundling takes some extra time.
190
- if (shouldBundleTypeScriptTemplate) {
189
+ // Bundle templates with helper imports. Helper-free templates stay on the
190
+ // faster path and rewrite the Figma import directly to require syntax.
191
+ if (shouldBundleTemplate) {
191
192
  // Confining resolution to the project dir
192
193
  const projectRoot = dir ? path_1.default.resolve(dir) : path_1.default.dirname(path_1.default.resolve(filePath));
193
194
  fileContent = await (0, raw_template_bundler_1.bundleTemplateWithHelpers)(filePath, projectRoot);
194
195
  }
195
- else if (filePath.endsWith('.ts')) {
196
- fileContent = transpileTypeScriptTemplate(filePath, fileContent);
196
+ else {
197
+ fileContent = rewriteFigmaImport(fileContent);
198
+ if (filePath.endsWith('.ts')) {
199
+ fileContent = transpileTypeScriptTemplate(fileContent);
200
+ }
197
201
  }
198
202
  // For batch templates, metadata comes from the batch entry instead of comments
199
203
  const component = batchOverrides?.component || fields.component;
@@ -1 +1 @@
1
- {"version":3,"file":"raw_templates.js","sourceRoot":"","sources":["../../src/connect/raw_templates.ts"],"names":[],"mappings":";;;;;;AAoCA,sCAaC;AAmKD,oCAqHC;AAzUD,4CAAmB;AACnB,gDAAuB;AACvB,4DAA2B;AAG3B,qEAAsF;AACtF,uCAAyD;AACzD,iEAO+B;AAE/B;;;;;;GAMG;AACH,MAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AALD,kDAKC;AAED;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,OAAe;IAC3C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,IAAI,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAA;YACb,CAAC;YACD,SAAQ;QACV,CAAC;QACD,mGAAmG;QACnG,MAAK;IACP,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,gBAAgB,GAAG,kDAAkD,CAAA;AAE3E,4EAA4E;AAC5E,qCAAqC;AACrC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAE9B,SAAS,6BAA6B,CAAC,QAAgB,EAAE,QAAgB;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACnE,IAAI,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,aAAa,QAAQ,QAAQ,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;YACpE,GAAG,oBAAoB,4BAA4B;YACnD,4EAA4E,CAC/E,CAAA;IACH,CAAC;AACH,CAAC;AAMD,0EAA0E;AAC1E,SAAS,uBAAuB,CAAC,QAAgB;IAC/C,OAAO,8CAAuB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;AACtE,CAAC;AAED;;;;GAIG;AACH,SAAS,iCAAiC,CACxC,QAAgB,EAChB,WAAmB;IAEnB,MAAM,UAAU,GAAG,oBAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,oBAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC3F,IAAI,wBAAwB,GAAG,KAAK,CAAA;IAEpC,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;QAC9C,IAAI,oBAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,IAAI,SAAS,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC;gBACvC,SAAQ;YACV,CAAC;YAED,MAAM,mBAAmB,GAAG,oBAAE,CAAC,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC;gBACvE,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI;gBAChC,CAAC,CAAC,EAAE,CAAA;YACN,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,YAAY,CAAA;YAEvF,IAAI,mBAAmB,KAAK,OAAO,EAAE,CAAC;gBACpC,MAAM,gBAAgB,GAAG,CAAC,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAA;gBACvD,MAAM,yBAAyB,GAAG,CAAC,CAAC,SAAS,CAAC,YAAY,EAAE,aAAa,CAAA;gBACzE,IAAI,CAAC,gBAAgB,IAAI,yBAAyB,EAAE,CAAC;oBACnD,MAAM,IAAA,6CAAsB,EAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;gBACpD,CAAC;gBACD,SAAQ;YACV,CAAC;YAED,IAAI,IAAA,2CAAoB,EAAC,mBAAmB,CAAC,EAAE,CAAC;gBAC9C,wBAAwB,GAAG,IAAI,CAAA;gBAC/B,SAAQ;YACV,CAAC;YAED,MAAM,IAAA,6CAAsB,EAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QACpD,CAAC;QAED,0EAA0E;QAC1E,IAAI,oBAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC5F,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,YAAY,CAAA;YACvF,MAAM,IAAI,KAAK,CACb,qEAAqE;gBACnE,YAAY,QAAQ,KAAK;gBACzB,KAAK,UAAU,MAAM;gBACrB,8EAA8E,CACjF,CAAA;QACH,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,YAAY,GAAG,CAAC,IAAa,EAAQ,EAAE;QAC3C,MAAM,OAAO,GAAG,IAAA,4CAAqB,EAAC,IAAI,CAAC,CAAA;QAC3C,IAAI,OAAO,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YACnC,MAAM,IAAA,2CAAoB,EAAC,OAAO,CAAC;gBACjC,CAAC,CAAC,IAAA,2CAAoB,EAAC,QAAQ,EAAE,OAAO,CAAC;gBACzC,CAAC,CAAC,IAAA,6CAAsB,EAAC,QAAQ,EAAE,YAAY,OAAO,IAAI,CAAC,CAAA;QAC/D,CAAC;QACD,oBAAE,CAAC,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACrC,CAAC,CAAA;IACD,YAAY,CAAC,UAAU,CAAC,CAAA;IAExB,OAAO,EAAE,wBAAwB,EAAE,CAAA;AACrC,CAAC;AAED,SAAS,2BAA2B,CAAC,QAAgB,EAAE,WAAmB;IACxE,IAAI,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACvC,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,gBAAgB,EAAE,gCAAgC,CAAC,CAAA;IACvF,CAAC;IAED,MAAM,MAAM,GAAG,oBAAE,CAAC,eAAe,CAAC,WAAW,EAAE;QAC7C,eAAe,EAAE;YACf,MAAM,EAAE,oBAAE,CAAC,UAAU,CAAC,MAAM;YAC5B,MAAM,EAAE,oBAAE,CAAC,YAAY,CAAC,MAAM;YAC9B,cAAc,EAAE,KAAK;SACtB;KACF,CAAC,CAAA;IAEF,OAAO,MAAM,CAAC,UAAU,CAAA;AAC1B,CAAC;AAED;;;;GAIG;AACH,SAAS,qBAAqB,CAAC,WAAmB;IAIhD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,MAAM,GAA0D,EAAE,CAAA;IACxE,IAAI,iBAAiB,GAAG,CAAC,CAAA;IAEzB,2DAA2D;IAC3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC5B,oCAAoC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAA;QAC/C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,GAAG,KAAK,CAAA;YACvC,MAAM,mBAAmB,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;YACnD,IACE,mBAAmB,KAAK,KAAK;gBAC7B,mBAAmB,KAAK,WAAW;gBACnC,mBAAmB,KAAK,QAAQ,EAChC,CAAC;gBACD,MAAM,CAAC,mBAAmB,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAA;YACjD,CAAC;YACD,iBAAiB,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;aAAM,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,0EAA0E;YAC1E,SAAQ;QACV,CAAC;aAAM,CAAC;YACN,+BAA+B;YAC/B,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAA;AACtC,CAAC;AAUM,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,KAAyB,EACzB,MAA0B,EAC1B,GAAY,EACZ,cAA+B;IAE/B,IAAI,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACpD,IAAI,8BAA8B,GAAG,KAAK,CAAA;IAE1C,wEAAwE;IACxE,iEAAiE;IACjE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAA;IAExE,MAAM,QAAQ,GAAG,cAAc,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,CAAA;IAElD,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAI,uBAAuB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,8BAA8B,GAAG,iCAAiC,CAChE,QAAQ,EACR,WAAW,CACZ,CAAC,wBAAwB,CAAA;IAC5B,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,wEAAwE;IACxE,6BAA6B;IAC7B,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,mBAAmB,CAC3B,YAAY,QAAQ,6GAA6G,CAClI,CAAA;IACH,CAAC;IAED,gFAAgF;IAChF,kCAAkC;IAClC,IAAI,8BAA8B,EAAE,CAAC;QACnC,0CAA0C;QAC1C,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;QAClF,WAAW,GAAG,MAAM,IAAA,gDAAyB,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;IACtE,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,WAAW,GAAG,2BAA2B,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;IAClE,CAAC;IAED,+EAA+E;IAC/E,MAAM,SAAS,GAAG,cAAc,EAAE,SAAS,IAAI,MAAM,CAAC,SAAS,CAAA;IAC/D,MAAM,MAAM,GAAG,cAAc,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAA;IAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,cAAc;YACZ,CAAC,CAAC,mCAAmC,cAAc,CAAC,aAAa,kEAAkE;YACnI,CAAC,CAAC,iCAAiC,QAAQ,2DAA2D,CACzG,CAAA;IACH,CAAC;IACD,IAAI,YAAY,GAAG,QAAQ,CAAA;IAE3B,qEAAqE;IACrE,2DAA2D;IAC3D,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC/C,IAAI,kBAAkB,GAAG,CAAC,CAAA;IAE1B,sEAAsE;IACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACtC,6CAA6C;QAC7C,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,SAAQ;QACV,CAAC;QACD,8CAA8C;QAC9C,kBAAkB,GAAG,CAAC,CAAA;QACtB,MAAK;IACP,CAAC;IAED,IAAI,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEnE,oFAAoF;IACpF,4EAA4E;IAC5E,yFAAyF;IACzF,iEAAiE;IACjE,IAAI,cAAc,EAAE,CAAC;QACnB,QAAQ,GAAG,iCAAiC,IAAI,CAAC,SAAS,CACxD,cAAc,CAAC,SAAS,CACzB,KAAK,QAAQ,EAAE,CAAA;IAClB,CAAC;IAED,8EAA8E;IAC9E,6EAA6E;IAC7E,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAEjD,6CAA6C;IAC7C,IAAI,MAAM,EAAE,wBAAwB,EAAE,CAAC;QACrC,YAAY,GAAG,IAAA,uCAA6B,EAAC,YAAY,EAAE,MAAM,CAAC,wBAAwB,CAAC,CAAA;IAC7F,CAAC;IAED,+DAA+D;IAC/D,MAAM,cAAc,GAAG,KAAK,IAAI,MAAM,EAAE,KAAK,IAAI,yCAAgB,CAAC,IAAI,CAAA;IAEtE,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,IAAA,kDAAyB,EAAC,cAAc,CAAC,CAAA;IAE9E,OAAO;QACL,SAAS,EAAE,YAAY;QACvB,SAAS;QACT,QAAQ;QACR,wDAAwD;QACxD,gEAAgE;QAChE,qCAAqC;QACrC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;QACpD,QAAQ;QACR,KAAK,EAAE,cAAc;QACrB,MAAM;QACN,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC5B,QAAQ,EAAE;YACR,UAAU,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC,OAAO;SAClD;KACF,CAAA;AACH,CAAC","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport ts from 'typescript'\nimport { CodeConnectJSON } from './figma_connect'\nimport { CodeConnectConfig } from './project'\nimport { CodeConnectLabel, getInferredLanguageForRaw } from './label_language_mapping'\nimport { applyDocumentUrlSubstitutions } from './helpers'\nimport {\n bundleTemplateWithHelpers,\n allowedHelperExtensions,\n isRelativeImportPath,\n unsupportedImportError,\n relativeRequireError,\n getRequireCallRequest,\n} from './raw_template_bundler'\n\n/**\n * Thrown when a raw template file has no `// url=` directive but contains the\n * string `codeProperties`. These files (e.g. Make's code component property\n * definitions) are not Code Connect, so callers skip them with a log instead of\n * failing. Every other file is handled exactly as before — a missing url is\n * still a hard error.\n */\nexport class CodePropertiesError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CodePropertiesError'\n }\n}\n\n/**\n * Returns true if the file content looks like a raw template file, i.e. its\n * leading comment block contains a `// url=`, `// component=`, or `// source=`\n * directive. Used to distinguish raw `.figma.ts` templates from React/HTML\n * Code Connect files that share the same extension.\n */\nexport function isRawTemplate(content: string): boolean {\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('//')) {\n if (/^\\/\\/\\s*(url|component|source)=/.test(trimmed)) {\n return true\n }\n continue\n }\n // First non-comment, non-blank line reached without finding a known directive — not a raw template\n break\n }\n return false\n}\n\n// Convert ESM import of 'figma' to require syntax. Supports: import figma from 'figma'\nconst figmaImportRegex = /^import\\s+figma\\s+from\\s+['\"]figma['\"]\\s*;?\\s*$/m\n\n// Matches the backend's max template size; we fail here rather than let the\n// server reject the publish request.\nconst MAX_TEMPLATE_SIZE_MB = 1\n\nfunction assertTemplateWithinSizeLimit(filePath: string, template: string): void {\n const sizeMb = Buffer.byteLength(template, 'utf-8') / (1024 * 1024)\n if (sizeMb > MAX_TEMPLATE_SIZE_MB) {\n throw new Error(\n `Template \"${filePath}\" is ${sizeMb.toFixed(2)}mb, which exceeds the ` +\n `${MAX_TEMPLATE_SIZE_MB}mb maximum template size. ` +\n `Reduce the template size, for example by removing unneeded helper imports.`,\n )\n }\n}\n\ninterface TypeScriptImportValidationResult {\n hasRelativeHelperImports: boolean\n}\n\n// Parserless entry templates may be authored in TypeScript or JavaScript.\nfunction isRawTemplateSourceFile(filePath: string): boolean {\n return allowedHelperExtensions.some((ext) => filePath.endsWith(ext))\n}\n\n/**\n * Validates a template entry's imports: only the default `figma` import,\n * type-only imports and relative helper imports are allowed. Returns whether\n * the entry imports helpers, so the caller knows whether to bundle.\n */\nfunction validateTypeScriptTemplateImports(\n filePath: string,\n fileContent: string,\n): TypeScriptImportValidationResult {\n const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true)\n let hasRelativeHelperImports = false\n\n for (const statement of sourceFile.statements) {\n if (ts.isImportDeclaration(statement)) {\n if (statement.importClause?.isTypeOnly) {\n continue\n }\n\n const moduleSpecifierText = ts.isStringLiteral(statement.moduleSpecifier)\n ? statement.moduleSpecifier.text\n : ''\n const importLine = statement.getText(sourceFile).split('\\n')[0]?.trim() ?? 'import ...'\n\n if (moduleSpecifierText === 'figma') {\n const hasDefaultImport = !!statement.importClause?.name\n const hasNamedOrNamespaceImport = !!statement.importClause?.namedBindings\n if (!hasDefaultImport || hasNamedOrNamespaceImport) {\n throw unsupportedImportError(filePath, importLine)\n }\n continue\n }\n\n if (isRelativeImportPath(moduleSpecifierText)) {\n hasRelativeHelperImports = true\n continue\n }\n\n throw unsupportedImportError(filePath, importLine)\n }\n\n // Re-exports aren't supported: the runtime only reads the default export.\n if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && !statement.isTypeOnly) {\n const exportLine = statement.getText(sourceFile).split('\\n')[0]?.trim() ?? 'export ...'\n throw new Error(\n `Template files do not support re-exports ('export ... from ...').\\n` +\n `Found in ${filePath}:\\n` +\n ` ${exportLine}\\n\\n` +\n `Import the helper instead (for example: import { helper } from './helpers').`,\n )\n }\n }\n\n // `require` is for the Figma API only: a pure ESM graph is what lets the\n // bundler emit a flat, tree-shaken template. Walking the AST (not the raw\n // text) keeps a `require(...)` inside an emitted snippet from matching.\n const visitRequire = (node: ts.Node): void => {\n const request = getRequireCallRequest(node)\n if (request && request !== 'figma') {\n throw isRelativeImportPath(request)\n ? relativeRequireError(filePath, request)\n : unsupportedImportError(filePath, `require('${request}')`)\n }\n ts.forEachChild(node, visitRequire)\n }\n visitRequire(sourceFile)\n\n return { hasRelativeHelperImports }\n}\n\nfunction transpileTypeScriptTemplate(filePath: string, fileContent: string): string {\n if (figmaImportRegex.test(fileContent)) {\n fileContent = fileContent.replace(figmaImportRegex, \"const figma = require('figma')\")\n }\n\n const result = ts.transpileModule(fileContent, {\n compilerOptions: {\n module: ts.ModuleKind.ESNext,\n target: ts.ScriptTarget.ES2021,\n removeComments: false,\n },\n })\n\n return result.outputText\n}\n\n/**\n * Extracts metadata fields (url, component, source) from the leading comments\n * of a raw template file. This must be done before transpilation because\n * TypeScript can remove comments that appear before type-only imports.\n */\nfunction extractMetadataFields(fileContent: string): {\n fields: { url?: string; component?: string; source?: string }\n templateStartLine: number\n} {\n const lines = fileContent.split('\\n')\n const fields: { url?: string; component?: string; source?: string } = {}\n let templateStartLine = 0\n\n // Parse consecutive comment lines at the start of the file\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim()\n // Match pattern: // fieldName=value\n const match = line.match(/^\\/\\/\\s*(\\w+)=(.+)$/)\n if (match) {\n const [, fieldName, fieldValue] = match\n const normalizedFieldName = fieldName.toLowerCase()\n if (\n normalizedFieldName === 'url' ||\n normalizedFieldName === 'component' ||\n normalizedFieldName === 'source'\n ) {\n fields[normalizedFieldName] = fieldValue.trim()\n }\n templateStartLine = i + 1\n } else if (line === '' || line.startsWith('//')) {\n // Allow blank lines or other comments, but don't increment template start\n continue\n } else {\n // First non-comment line found\n break\n }\n }\n\n return { fields, templateStartLine }\n}\n\nexport interface BatchOverrides {\n url: string\n source?: string\n component?: string\n batchData: Record<string, any>\n batchFilePath: string\n}\n\nexport async function parseRawFile(\n filePath: string,\n label: string | undefined,\n config?: CodeConnectConfig,\n dir?: string,\n batchOverrides?: BatchOverrides,\n): Promise<CodeConnectJSON> {\n let fileContent = fs.readFileSync(filePath, 'utf-8')\n let shouldBundleTypeScriptTemplate = false\n\n // Extract metadata fields BEFORE transpilation to avoid losing comments\n // that appear before type-only imports (which TypeScript erases)\n const { fields, templateStartLine } = extractMetadataFields(fileContent)\n\n const figmaUrl = batchOverrides?.url || fields.url\n\n // Validate imports first, before the `codeProperties` skip guard below, so an\n // unsupported import is always a hard error rather than silently swallowed.\n if (isRawTemplateSourceFile(filePath)) {\n shouldBundleTypeScriptTemplate = validateTypeScriptTemplateImports(\n filePath,\n fileContent,\n ).hasRelativeHelperImports\n }\n\n // A file with no // url= directive that contains the string `codeProperties`\n // is not Code Connect (e.g. Make's code component property definitions). Skip\n // it (callers log and continue) instead of failing. Every other file is\n // handled exactly as before.\n if (!figmaUrl && fileContent.includes('codeProperties')) {\n throw new CodePropertiesError(\n `Skipping ${filePath}: file has no // url= directive and contains \"codeProperties\", so it is not treated as a Code Connect file.`,\n )\n }\n\n // Bundle when helpers are present; otherwise transpile TS and emit JS verbatim.\n // Bundling takes some extra time.\n if (shouldBundleTypeScriptTemplate) {\n // Confining resolution to the project dir\n const projectRoot = dir ? path.resolve(dir) : path.dirname(path.resolve(filePath))\n fileContent = await bundleTemplateWithHelpers(filePath, projectRoot)\n } else if (filePath.endsWith('.ts')) {\n fileContent = transpileTypeScriptTemplate(filePath, fileContent)\n }\n\n // For batch templates, metadata comes from the batch entry instead of comments\n const component = batchOverrides?.component || fields.component\n const source = batchOverrides?.source || fields.source || ''\n\n if (!figmaUrl) {\n throw new Error(\n batchOverrides\n ? `Missing required \"url\" field in ${batchOverrides.batchFilePath}. Please add \"url\" to each entry in your .figma.batch.json file.`\n : `Missing required url field in ${filePath}. Please add a // url=... comment to the top of the file.`,\n )\n }\n let figmaNodeUrl = figmaUrl\n\n // Extract template from the transpiled content, starting at the line\n // where the metadata comments ended in the original source\n const transpiledLines = fileContent.split('\\n')\n let templateStartIndex = 0\n\n // Skip lines until we've passed the original metadata comment section\n for (let i = 0; i < transpiledLines.length; i++) {\n const line = transpiledLines[i].trim()\n // Skip blank lines and comments at the start\n if (line === '' || line.startsWith('//')) {\n continue\n }\n // First non-comment line in transpiled output\n templateStartIndex = i\n break\n }\n\n let template = transpiledLines.slice(templateStartIndex).join('\\n')\n\n // For batch templates, set globalThis['__FIGMA_BATCH'] so the runtime exposes it as\n // figma.batch. Using globalThis rather than a const so it's accessible from\n // __FIGMA_CODE_CONNECT_REQUIRE's closure regardless of where require('figma') is called.\n // (globalThis works in both browser and Node.js, unlike window.)\n if (batchOverrides) {\n template = `globalThis['__FIGMA_BATCH'] = ${JSON.stringify(\n batchOverrides.batchData,\n )}\\n${template}`\n }\n\n // Check the final template (incl. any bundled helpers and batch data) against\n // the backend's size cap so an oversized template fails here, not on upload.\n assertTemplateWithinSizeLimit(filePath, template)\n\n // Apply documentUrlSubstitutions if provided\n if (config?.documentUrlSubstitutions) {\n figmaNodeUrl = applyDocumentUrlSubstitutions(figmaNodeUrl, config.documentUrlSubstitutions)\n }\n\n // Determine effective label from parameter, config, or default\n const effectiveLabel = label || config?.label || CodeConnectLabel.Code\n\n const language = config?.language || getInferredLanguageForRaw(effectiveLabel)\n\n return {\n figmaNode: figmaNodeUrl,\n component,\n template,\n // nestable by default unless user specifies in template\n // (templateData.nestable AND template.metadata.nestable need to\n // be true for instance to be nested)\n templateData: { nestable: true, isParserless: true },\n language,\n label: effectiveLabel,\n source,\n sourceLocation: { line: -1 },\n metadata: {\n cliVersion: require('../../package.json').version,\n },\n }\n}\n"]}
1
+ {"version":3,"file":"raw_templates.js","sourceRoot":"","sources":["../../src/connect/raw_templates.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport ts from 'typescript'\nimport { CodeConnectJSON } from './figma_connect'\nimport { CodeConnectConfig } from './project'\nimport { CodeConnectLabel, getInferredLanguageForRaw } from './label_language_mapping'\nimport { applyDocumentUrlSubstitutions } from './helpers'\nimport {\n bundleTemplateWithHelpers,\n allowedHelperExtensions,\n isRelativeImportPath,\n unsupportedImportError,\n relativeRequireError,\n getRequireCallRequest,\n} from './raw_template_bundler'\n\n/**\n * Thrown when a raw template file has no `// url=` directive but contains the\n * string `codeProperties`. These files (e.g. Make's code component property\n * definitions) are not Code Connect, so callers skip them with a log instead of\n * failing. Every other file is handled exactly as before — a missing url is\n * still a hard error.\n */\nexport class CodePropertiesError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CodePropertiesError'\n }\n}\n\n/**\n * Returns true if the file content looks like a raw template file, i.e. its\n * leading comment block contains a `// url=`, `// component=`, or `// source=`\n * directive. Used to distinguish raw `.figma.ts` templates from React/HTML\n * Code Connect files that share the same extension.\n */\nexport function isRawTemplate(content: string): boolean {\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('//')) {\n if (/^\\/\\/\\s*(url|component|source)=/.test(trimmed)) {\n return true\n }\n continue\n }\n // First non-comment, non-blank line reached without finding a known directive — not a raw template\n break\n }\n return false\n}\n\n// Convert the supported ESM Figma import to the runtime's require syntax when\n// the template does not otherwise need bundling.\nconst figmaImportRegex =\n /^import[ \\t]+figma[ \\t]+from[ \\t]+['\"]figma['\"][ \\t]*;?[ \\t]*(?:\\/\\/[^\\r\\n]*)?$/m\n\n// Matches the backend's max template size; we fail here rather than let the\n// server reject the publish request.\nconst MAX_TEMPLATE_SIZE_MB = 1\n\nfunction assertTemplateWithinSizeLimit(filePath: string, template: string): void {\n const sizeMb = Buffer.byteLength(template, 'utf-8') / (1024 * 1024)\n if (sizeMb > MAX_TEMPLATE_SIZE_MB) {\n throw new Error(\n `Template \"${filePath}\" is ${sizeMb.toFixed(2)}mb, which exceeds the ` +\n `${MAX_TEMPLATE_SIZE_MB}mb maximum template size. ` +\n `Reduce the template size, for example by removing unneeded helper imports.`,\n )\n }\n}\n\ninterface TemplateImportValidationResult {\n hasRelativeHelperImports: boolean\n}\n\n// Parserless entry templates may be authored in TypeScript or JavaScript.\nfunction isRawTemplateSourceFile(filePath: string): boolean {\n return allowedHelperExtensions.some((ext) => filePath.endsWith(ext))\n}\n\n/**\n * Validates a template entry's imports: only the default `figma` import,\n * type-only imports and relative helper imports are allowed. Returns whether\n * the entry imports helpers, so the caller knows whether to bundle it.\n */\nfunction validateTemplateImports(\n filePath: string,\n fileContent: string,\n): TemplateImportValidationResult {\n const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true)\n let hasRelativeHelperImports = false\n\n for (const statement of sourceFile.statements) {\n if (ts.isImportDeclaration(statement)) {\n if (statement.importClause?.isTypeOnly) {\n continue\n }\n\n const moduleSpecifierText = ts.isStringLiteral(statement.moduleSpecifier)\n ? statement.moduleSpecifier.text\n : ''\n const importLine = statement.getText(sourceFile).split('\\n')[0]?.trim() ?? 'import ...'\n\n if (moduleSpecifierText === 'figma') {\n const hasDefaultImport = !!statement.importClause?.name\n const hasNamedOrNamespaceImport = !!statement.importClause?.namedBindings\n if (!hasDefaultImport || hasNamedOrNamespaceImport) {\n throw unsupportedImportError(filePath, importLine)\n }\n continue\n }\n\n if (isRelativeImportPath(moduleSpecifierText)) {\n hasRelativeHelperImports = true\n continue\n }\n\n throw unsupportedImportError(filePath, importLine)\n }\n\n // Re-exports aren't supported: the runtime only reads the default export.\n if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && !statement.isTypeOnly) {\n const exportLine = statement.getText(sourceFile).split('\\n')[0]?.trim() ?? 'export ...'\n throw new Error(\n `Template files do not support re-exports ('export ... from ...').\\n` +\n `Found in ${filePath}:\\n` +\n ` ${exportLine}\\n\\n` +\n `Import the helper instead (for example: import { helper } from './helpers').`,\n )\n }\n }\n\n // `require` is for the Figma API only: a pure ESM graph is what lets the\n // bundler emit a flat, tree-shaken template. Walking the AST (not the raw\n // text) keeps a `require(...)` inside an emitted snippet from matching.\n const visitRequire = (node: ts.Node): void => {\n const request = getRequireCallRequest(node)\n if (request && request !== 'figma') {\n throw isRelativeImportPath(request)\n ? relativeRequireError(filePath, request)\n : unsupportedImportError(filePath, `require('${request}')`)\n }\n ts.forEachChild(node, visitRequire)\n }\n visitRequire(sourceFile)\n\n return { hasRelativeHelperImports }\n}\n\nfunction rewriteFigmaImport(fileContent: string): string {\n return fileContent.replace(figmaImportRegex, \"const figma = require('figma')\")\n}\n\nfunction transpileTypeScriptTemplate(fileContent: string): string {\n const result = ts.transpileModule(fileContent, {\n compilerOptions: {\n module: ts.ModuleKind.ESNext,\n target: ts.ScriptTarget.ES2021,\n removeComments: false,\n },\n })\n\n return result.outputText\n}\n\n/**\n * Extracts metadata fields (url, component, source) from the leading comments\n * of a raw template file. This must be done before transpilation because\n * TypeScript can remove comments that appear before type-only imports.\n */\nfunction extractMetadataFields(fileContent: string): {\n fields: { url?: string; component?: string; source?: string }\n templateStartLine: number\n} {\n const lines = fileContent.split('\\n')\n const fields: { url?: string; component?: string; source?: string } = {}\n let templateStartLine = 0\n\n // Parse consecutive comment lines at the start of the file\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim()\n // Match pattern: // fieldName=value\n const match = line.match(/^\\/\\/\\s*(\\w+)=(.+)$/)\n if (match) {\n const [, fieldName, fieldValue] = match\n const normalizedFieldName = fieldName.toLowerCase()\n if (\n normalizedFieldName === 'url' ||\n normalizedFieldName === 'component' ||\n normalizedFieldName === 'source'\n ) {\n fields[normalizedFieldName] = fieldValue.trim()\n }\n templateStartLine = i + 1\n } else if (line === '' || line.startsWith('//')) {\n // Allow blank lines or other comments, but don't increment template start\n continue\n } else {\n // First non-comment line found\n break\n }\n }\n\n return { fields, templateStartLine }\n}\n\nexport interface BatchOverrides {\n url: string\n source?: string\n component?: string\n batchData: Record<string, any>\n batchFilePath: string\n}\n\nexport async function parseRawFile(\n filePath: string,\n label: string | undefined,\n config?: CodeConnectConfig,\n dir?: string,\n batchOverrides?: BatchOverrides,\n): Promise<CodeConnectJSON> {\n let fileContent = fs.readFileSync(filePath, 'utf-8')\n let shouldBundleTemplate = false\n\n // Extract metadata fields BEFORE transpilation to avoid losing comments\n // that appear before type-only imports (which TypeScript erases)\n const { fields, templateStartLine } = extractMetadataFields(fileContent)\n\n const figmaUrl = batchOverrides?.url || fields.url\n\n // Validate imports first, before the `codeProperties` skip guard below, so an\n // unsupported import is always a hard error rather than silently swallowed.\n if (isRawTemplateSourceFile(filePath)) {\n shouldBundleTemplate = validateTemplateImports(filePath, fileContent).hasRelativeHelperImports\n }\n\n // A file with no // url= directive that contains the string `codeProperties`\n // is not Code Connect (e.g. Make's code component property definitions). Skip\n // it (callers log and continue) instead of failing. Every other file is\n // handled exactly as before.\n if (!figmaUrl && fileContent.includes('codeProperties')) {\n throw new CodePropertiesError(\n `Skipping ${filePath}: file has no // url= directive and contains \"codeProperties\", so it is not treated as a Code Connect file.`,\n )\n }\n\n // Bundle templates with helper imports. Helper-free templates stay on the\n // faster path and rewrite the Figma import directly to require syntax.\n if (shouldBundleTemplate) {\n // Confining resolution to the project dir\n const projectRoot = dir ? path.resolve(dir) : path.dirname(path.resolve(filePath))\n fileContent = await bundleTemplateWithHelpers(filePath, projectRoot)\n } else {\n fileContent = rewriteFigmaImport(fileContent)\n if (filePath.endsWith('.ts')) {\n fileContent = transpileTypeScriptTemplate(fileContent)\n }\n }\n\n // For batch templates, metadata comes from the batch entry instead of comments\n const component = batchOverrides?.component || fields.component\n const source = batchOverrides?.source || fields.source || ''\n\n if (!figmaUrl) {\n throw new Error(\n batchOverrides\n ? `Missing required \"url\" field in ${batchOverrides.batchFilePath}. Please add \"url\" to each entry in your .figma.batch.json file.`\n : `Missing required url field in ${filePath}. Please add a // url=... comment to the top of the file.`,\n )\n }\n let figmaNodeUrl = figmaUrl\n\n // Extract template from the transpiled content, starting at the line\n // where the metadata comments ended in the original source\n const transpiledLines = fileContent.split('\\n')\n let templateStartIndex = 0\n\n // Skip lines until we've passed the original metadata comment section\n for (let i = 0; i < transpiledLines.length; i++) {\n const line = transpiledLines[i].trim()\n // Skip blank lines and comments at the start\n if (line === '' || line.startsWith('//')) {\n continue\n }\n // First non-comment line in transpiled output\n templateStartIndex = i\n break\n }\n\n let template = transpiledLines.slice(templateStartIndex).join('\\n')\n\n // For batch templates, set globalThis['__FIGMA_BATCH'] so the runtime exposes it as\n // figma.batch. Using globalThis rather than a const so it's accessible from\n // __FIGMA_CODE_CONNECT_REQUIRE's closure regardless of where require('figma') is called.\n // (globalThis works in both browser and Node.js, unlike window.)\n if (batchOverrides) {\n template = `globalThis['__FIGMA_BATCH'] = ${JSON.stringify(\n batchOverrides.batchData,\n )}\\n${template}`\n }\n\n // Check the final template (incl. any bundled helpers and batch data) against\n // the backend's size cap so an oversized template fails here, not on upload.\n assertTemplateWithinSizeLimit(filePath, template)\n\n // Apply documentUrlSubstitutions if provided\n if (config?.documentUrlSubstitutions) {\n figmaNodeUrl = applyDocumentUrlSubstitutions(figmaNodeUrl, config.documentUrlSubstitutions)\n }\n\n // Determine effective label from parameter, config, or default\n const effectiveLabel = label || config?.label || CodeConnectLabel.Code\n\n const language = config?.language || getInferredLanguageForRaw(effectiveLabel)\n\n return {\n figmaNode: figmaNodeUrl,\n component,\n template,\n // nestable by default unless user specifies in template\n // (templateData.nestable AND template.metadata.nestable need to\n // be true for instance to be nested)\n templateData: { nestable: true, isParserless: true },\n language,\n label: effectiveLabel,\n source,\n sourceLocation: { line: -1 },\n metadata: {\n cliVersion: require('../../package.json').version,\n },\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@figma/code-connect",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "A tool for connecting your design system components in code with your design system in Figma",
5
5
  "keywords": [],
6
6
  "author": "Figma",