openfig-cli 0.3.19 → 0.3.20

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.
@@ -278,12 +278,64 @@ export class FigDeck {
278
278
  });
279
279
  }
280
280
 
281
+ /**
282
+ * Validate deck integrity before saving. Warns about issues that would
283
+ * cause Figma to fail silently (blank slides, missing symbols, etc.).
284
+ */
285
+ validate() {
286
+ const warnings = [];
287
+ const symbols = this.getSymbols().filter(s => s.phase !== 'REMOVED');
288
+
289
+ // Check for variant name / variantPropSpecs mismatch
290
+ const byKey = new Map();
291
+ for (const sym of symbols) {
292
+ if (!sym.componentKey) continue;
293
+ if (!byKey.has(sym.componentKey)) byKey.set(sym.componentKey, []);
294
+ byKey.get(sym.componentKey).push(sym);
295
+ }
296
+ for (const [key, variants] of byKey) {
297
+ if (variants.length < 2) continue;
298
+ const specValues = new Set();
299
+ for (const sym of variants) {
300
+ if (sym.variantPropSpecs) {
301
+ for (const spec of sym.variantPropSpecs) specValues.add(spec.value);
302
+ }
303
+ }
304
+ for (const sym of variants) {
305
+ const parts = (sym.name || '').split(', ').map(p => p.split('=')[1]).filter(Boolean);
306
+ for (const val of parts) {
307
+ if (!specValues.has(val)) {
308
+ const id = `${sym.guid.sessionID}:${sym.guid.localID}`;
309
+ warnings.push(`SYMBOL ${id} "${sym.name}": variant value "${val}" not in variantPropSpecs — Figma will show blank slides`);
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ // Check for instances referencing missing symbols
316
+ for (const node of this.message.nodeChanges) {
317
+ if (node.type !== 'INSTANCE' || node.phase === 'REMOVED') continue;
318
+ const sid = node.symbolData?.symbolID;
319
+ if (!sid) continue;
320
+ const symId = `${sid.sessionID}:${sid.localID}`;
321
+ const sym = this.getNode(symId);
322
+ if (!sym || sym.type !== 'SYMBOL') {
323
+ const nid = `${node.guid.sessionID}:${node.guid.localID}`;
324
+ warnings.push(`INSTANCE ${nid} "${node.name}": references missing SYMBOL ${symId}`);
325
+ }
326
+ }
327
+
328
+ for (const w of warnings) console.warn(`⚠️ ${w}`);
329
+ return warnings;
330
+ }
331
+
281
332
  /**
282
333
  * Save as a .deck (ZIP archive).
283
334
  * @param {string} outPath - Output file path
284
335
  * @param {object} opts - { imagesDir, thumbnail, meta }
285
336
  */
286
337
  async saveDeck(outPath, opts = {}) {
338
+ this.validate();
287
339
  const figBuf = await this.encodeFig();
288
340
  const absOut = resolve(outPath);
289
341
 
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.2",
3
3
  "name": "openfig",
4
- "version": "0.3.19",
4
+ "version": "0.3.20",
5
5
  "description": "Open-source tools for Figma file parsing and rendering",
6
6
  "author": {
7
7
  "name": "OpenFig Contributors"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfig-cli",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "description": "OpenFig — Open-source tools for Figma file parsing and rendering",
5
5
  "type": "module",
6
6
  "bin": {