Skip to content

feat: add YYJSON - #47

Merged
alexey1312 merged 16 commits into
mainfrom
alexey1312/exfig-json-codec
Feb 5, 2026
Merged

feat: add YYJSON#47
alexey1312 merged 16 commits into
mainfrom
alexey1312/exfig-json-codec

Conversation

@alexey1312

Copy link
Copy Markdown
Collaborator

This pull request introduces a major refactor to the project's JSON serialization and deserialization logic, replacing all usages of the Foundation JSONEncoder/JSONDecoder with the high-performance YYJSON library via a new centralized JSONCodec utility. This change improves performance, ensures deterministic output where needed, and standardizes JSON handling across the codebase. Additionally, explicit CodingKeys are added to several Figma API models for robust key mapping, and the new dependency is integrated into the build system.

Core Infrastructure Updates

  • Added swift-yyjson as a dependency in Package.swift and updated the ExFigCore target to use the YYJSON product. [1] [2]
  • Introduced Sources/ExFigCore/JSON/JSONCodec.swift, a centralized utility for encoding and decoding JSON using YYJSON, including support for pretty-printing and sorted keys for deterministic output.

Migration to YYJSON

  • Replaced all usages of JSONEncoder/JSONDecoder with JSONCodec in cache, output, and command files, ensuring consistent and performant JSON handling. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]

Figma API Model Improvements

  • Added explicit CodingKeys enums to Figma API models (Component, ContainingFrame, and Style) to ensure correct mapping of snake_case fields from the API to Swift properties. [1] [2] [3]

Codebase Integration

  • Updated imports throughout the codebase to use ExFigCore and YYJSON where necessary for new JSON handling. [1] [2] [3] [4] [5] [6] [7] [8]

Backward Compatibility and Output Stability

  • Ensured that output files and caches are written with pretty-printing and sorted keys for stable, deterministic output, and that legacy migration logic is maintained. [1] [2] [3]

Let me know if you'd like to dive deeper into any specific part of this migration or how to use the new JSONCodec utility!

alexey1312 and others added 7 commits February 3, 2026 20:35
- Add swift-yyjson dependency for high-performance JSON encoding/decoding
- Create JSONCodec enum in ExFigCore with factory and convenience methods
- Add explicit CodingKeys to Variables models (YYJSON doesn't recursively
  apply keyDecodingStrategy to nested structures in arrays/dictionaries)
- Add comprehensive tests for JSONCodec functionality

WIP: FigmaAPI migration in progress - remaining models need CodingKeys

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

# Conflicts:
#	Package.resolved
- Add explicit CodingKeys to Variables, Node, Style, and Components models
- Update BaseEndpoint to use JSONCodec.decode()
- Update UpdateVariablesEndpoint to use JSONCodec.encode()
- Update test fixtures and tests to use snake_case JSON keys

WIP: Some tests still failing, need to update remaining test JSON to snake_case

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove CodingKeys from Node.swift (TypeStyle, Document, Effect, Paint)
- Remove CodingKeys from Variables.swift (already camelCase)
- Remove deprecated decodeFigma() from JSONCodec
- Update all fixtures and tests to camelCase JSON

Figma API uses camelCase keys (strokeWeight, blendMode, modeId),
not snake_case as previously assumed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ExportCheckpoint, BatchCheckpoint: use JSONCodec with .iso8601 date strategy
- ImageTrackingCache: use JSONCodec for load/save
- NodeHasher: use JSONCodec.encodeSorted() for deterministic hashing
- RawExporter, Batch: use JSONCodec + YYJSONSerialization for sorted keys
- PKLEvaluator: use JSONCodec.decode() for config parsing
- Test helpers: use JSONCodec and camelCase JSON (matches real Figma API)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

# Conflicts:
#	Sources/ExFig/PKL/PKLEvaluator.swift
- All FigmaAPI tests pass (217 tests)
- All batch tests pass (89 tests)
- Add swift-yyjson to CLAUDE.md dependencies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

# Conflicts:
#	CLAUDE.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

chore(openspec): archive add-yyjson-codec

# Conflicts:
#	CLAUDE.md
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @alexey1312, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request undertakes a significant refactor of the project's JSON processing infrastructure. By migrating from Foundation's default JSON coders to the YYJSON library, the project gains substantial performance improvements and reduced memory allocations. A new JSONCodec utility now serves as a single, consistent interface for all JSON encoding and decoding, ensuring standardized behavior and enabling deterministic output where required for caching and hashing. The changes also include explicit key mapping for specific Figma API models to maintain data integrity.

Highlights

  • Performance Improvement: Replaced Foundation's JSONEncoder/JSONDecoder with the high-performance YYJSON library to optimize JSON serialization and deserialization.
  • Centralized JSON Handling: Introduced a new JSONCodec utility in ExFigCore to standardize and centralize all JSON operations across the codebase.
  • Deterministic Output: Implemented sorted keys for JSON encoding, particularly for hashing and caching, to ensure stable and deterministic output.
  • Figma API Model Enhancements: Added explicit CodingKeys to several Figma API models (Component, ContainingFrame, Style) to accurately map snake_case fields from the API to Swift properties.
  • Dependency Integration: Integrated the swift-yyjson library as a new dependency within the project's Package.swift and Package.resolved.
Changelog
  • Package.resolved
    • Updated to include the swift-yyjson dependency at version 0.3.0.
  • Package.swift
    • Added swift-yyjson as a package dependency.
    • Linked YYJSON product to the ExFigCore target.
    • Added ExFigCore as a dependency for the FigmaAPI target.
  • Sources/ExFig/Cache/BatchCheckpoint.swift
    • Migrated JSON encoding and decoding for BatchCheckpoint to use JSONCodec.makeEncoder() and JSONCodec.makeDecoder().
    • Adjusted encoder's outputFormatting to writeOptions for pretty-printing.
  • Sources/ExFig/Cache/ExportCheckpoint.swift
    • Replaced Foundation JSONEncoder/JSONDecoder with JSONCodec for ExportCheckpoint serialization.
  • Sources/ExFig/Cache/ImageTrackingCache.swift
    • Updated cache loading and saving to utilize JSONCodec.decode and JSONCodec.makeEncoder.
  • Sources/ExFig/Cache/NodeHasher.swift
    • Replaced direct JSONEncoder usage with JSONCodec.encodeSorted to ensure deterministic JSON output for hashing.
  • Sources/ExFig/Output/RawExporter.swift
    • Refactored JSON serialization to use JSONCodec.encode and YYJSONSerialization for flexible pretty-printing and sorted keys.
  • Sources/ExFig/Subcommands/Batch.swift
    • Updated batch report generation to use JSONCodec.encode and YYJSONSerialization for stable, pretty-printed output.
  • Sources/ExFigCore/JSON/JSONCodec.swift
    • Added new utility enum JSONCodec providing static methods for YYJSONEncoder and YYJSONDecoder instantiation.
    • Introduced convenience encode/decode functions, including encodeSorted for deterministic output.
  • Sources/FigmaAPI/Endpoint/BaseEndpoint.swift
    • Replaced the custom JSONDecoder.default with JSONCodec.decode for handling Figma API responses.
    • Removed the JSONDecoder.default extension as explicit CodingKeys are now used in models.
  • Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift
    • Added private enum CodingKeys to Component and ContainingFrame structs for explicit mapping of snake_case JSON keys to camelCase Swift properties.
  • Sources/FigmaAPI/Endpoint/UpdateVariablesEndpoint.swift
    • Switched from JSONEncoder to JSONCodec.encode for serializing the request body.
  • Sources/FigmaAPI/Model/Style.swift
    • Added private enum CodingKeys to the Style struct for explicit mapping of snake_case JSON keys.
  • Tests/ExFigCoreTests/JSONCodecTests.swift
    • Added a comprehensive suite of unit tests for the new JSONCodec utility, covering basic encode/decode, pretty printing, sorted keys, and handling of camelCase JSON.
  • Tests/ExFigTests/Helpers/TestHelpers.swift
    • Updated various test helper functions to use JSONCodec.decode for JSON parsing.
    • Adjusted mock JSON strings to reflect camelCase keys where appropriate for VariablesMeta creation.
  • Tests/ExFigTests/Sync/CodeSyntaxSyncerTests.swift
    • Modified test setup to use JSONCodec.decode for JSON parsing.
    • Updated mock JSON structures to align with camelCase key conventions for Figma API responses.
  • Tests/FigmaAPITests/Fixtures/NodesResponse.json
    • Updated JSON fixture to use camelCase keys within the style object (e.g., fontFamily instead of font_family).
  • Tests/FigmaAPITests/Fixtures/VariablesResponse.json
    • Updated JSON fixture to use camelCase keys for variable collections and modes (e.g., variableCollections, defaultModeId, modeId).
  • Tests/FigmaAPITests/Helpers/FixtureLoader.swift
    • Removed the keyDecodingStrategy = .convertFromSnakeCase from the JSONDecoder as models now handle this explicitly via CodingKeys.
  • Tests/FigmaAPITests/VariablesEndpointTests.swift
    • Added new test cases to directly verify JSONCodec's decoding capabilities for Figma API structures.
    • Confirmed Foundation.JSONDecoder still works with camelCase JSON.
  • openspec/changes/archive/2026-02-03-add-yyjson-codec/design.md
    • New file documenting the architectural design, JSONCodec implementation, migration patterns, and key considerations for the YYJSON integration.
  • openspec/changes/archive/2026-02-03-add-yyjson-codec/proposal.md
    • New file outlining the proposal, motivation (performance), scope, design decisions, risks, and success criteria for adding the YYJSON codec.
  • openspec/changes/archive/2026-02-03-add-yyjson-codec/specs/json-codec/spec.md
    • New file detailing the functional requirements and scenarios for the JSONCodec capability.
  • openspec/changes/archive/2026-02-03-add-yyjson-codec/tasks.md
    • New file listing the phased tasks completed during the implementation of the YYJSON codec.
  • openspec/specs/json-codec/spec.md
    • New file, a copy of the specification for the json-codec capability.
Activity
  • This pull request introduces a major architectural refactor, replacing the project's JSON serialization and deserialization logic with the high-performance YYJSON library.
  • A new JSONCodec utility has been created in ExFigCore to centralize and standardize all JSON handling, ensuring consistency and performance.
  • The author has provided extensive documentation, including a design document, a proposal, a specification, and a detailed task list, indicating a well-planned and executed migration.
  • Existing usages of JSONEncoder/JSONDecoder across various modules (cache, output, commands, Figma API) have been systematically migrated to use the new JSONCodec.
  • Explicit CodingKeys were added to several Figma API models to ensure correct mapping of snake_case fields, enhancing robustness.
  • Comprehensive unit tests for the new JSONCodec have been added, and existing tests were updated to reflect the changes and ensure backward compatibility where applicable.
  • The author invites further discussion on the migration or the usage of the new JSONCodec utility, demonstrating readiness for feedback.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a significant and well-executed refactor, replacing Foundation's JSON handling with the high-performance YYJSON library. The introduction of a centralized JSONCodec is a great step towards standardizing JSON operations across the codebase. The migration appears thorough, covering application code, tests, and fixtures.

My main feedback concerns the handling of sorted keys for cache files. The sortedKeys option was dropped in a few places, which makes the cache output non-deterministic. I've suggested a fix to restore this behavior for consistency and to align with the PR's goal of deterministic output. I also found a minor discrepancy between the design documentation and the final implementation, which would be good to align.

Comment thread Sources/ExFig/Cache/BatchCheckpoint.swift
Comment thread Sources/ExFig/Cache/ExportCheckpoint.swift
Comment thread Sources/ExFig/Cache/ImageTrackingCache.swift
Comment thread openspec/changes/archive/2026-02-03-add-yyjson-codec/design.md Outdated
alexey1312 and others added 8 commits February 3, 2026 21:13
Switch from Foundation JSON serialization to JSONCodec for performance
and determinism. Add new methods for sorted and pretty-printed JSON to
simplify xcasset and hashing workflows.

Introduce Logging for better error diagnostics in load/serialize ops.
Ensure invalid cache or encoding issues are logged, aiding debugging.
Updated Xcode and NodeHasher modules to log exceptions explicitly.
Updated test cases to fully cover error scenarios in JSONCodec.
When converting non-HTTP/non-URLError errors to FigmaAPIError,
preserve the original error's localizedDescription in the new
underlyingMessage property. This provides better debugging context
for errors like JSON decoding failures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Error.bestDescription extension that prefers CustomStringConvertible
over localizedDescription for better error messages from types like
YYJSONError that don't implement LocalizedError.

Also update ContainingFrame to use standard JSON naming (nodeId vs nodeID)
and add missing optional fields (pageId, backgroundColor).
Separate file version tracking from experimental granular node hashing.
Now --cache enables file versions tracking, while node hashes are only
stored when --experimental-granular-cache is also set.
Update the swift-yyjson package version from 0.3.0 to 0.3.1 in both
Package.resolved and Package.swift. This update ensures we have the
latest bug fixes and improvements available in the package.
Introduce a cache for parent directory contents to optimize case
mismatch detection on case-insensitive file systems. The cache
stores lowercase-to-URL mappings, reducing redundant directory
scans. This change improves efficiency, particularly with
large batches of files, by preventing multiple scans of the same
parent directory, speeding up both sequential and parallel writes.
@alexey1312
alexey1312 force-pushed the alexey1312/exfig-json-codec branch 2 times, most recently from 927d944 to c89bd29 Compare February 5, 2026 13:39
Update swift-yyjson to 0.4.0 which adds sortedKeys support to encoder.
Simplify encodeSorted and encodePrettySorted by removing two-step
encode-then-serialize approach. Update tests for new 2-space indent
format and improved hash determinism.
@alexey1312
alexey1312 force-pushed the alexey1312/exfig-json-codec branch from c89bd29 to 3d870a8 Compare February 5, 2026 14:06
@alexey1312
alexey1312 merged commit a607cf9 into main Feb 5, 2026
3 checks passed
@alexey1312
alexey1312 deleted the alexey1312/exfig-json-codec branch February 5, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant