Skip to content

Consolidate runtime adapters (FigmaPluginNodes/Foundations) into specs-from-figma #116

@nathanacurtis

Description

@nathanacurtis

Summary

Consolidate the runtime adapter implementations for the processing engine into specs-from-figma, alongside the FigmaNodes/FigmaFoundations interfaces and the existing REST adapters. Today the plugin adapters (FigmaPluginNodes, FigmaPluginFoundations) live in specs-plugin-2, split from their REST counterparts and the interfaces they implement. This split has produced a recurring, hard-to-catch class of bug.

Why

The engine has two planes of node access:

  1. Lookup/scanning — goes through the shared FigmaNodes abstraction (getPageSiblings, getNodeById, …). Interface + FigmaRestNodes live in specs-from-figma; FigmaPluginNodes lives in specs-plugin-2.
  2. Tree traversal / property access — engine code (e.g. SlotDetector) walks raw node trees and reads runtime-specific properties directly (.componentId, .children, getMainComponentAsync). This plane is not abstracted, and the node shape differs by runtime (REST JSON vs live Figma SceneNode).

Because the plugin adapter lives in a different repo and is never exercised by specs-from-figma's tests (which mock nodes in the REST shape), plane-2 divergences ship green in the engine and break only at runtime in the plugin. Two instances of exactly this:

  • SlotDetector gated instance recursion on .componentId (REST-only) → the plugin never descended into instances, capturing no nested slot content. (Just fixed by falling back to getMainComponentAsync().id.)
  • getPageSiblings/scan results needed mainComponentId populated on plugin INSTANCE nodes → earlier fix in FigmaPluginNodes.

Proposal

Move the plugin adapters into specs-from-figma:

specs-from-figma/src/Runtime/Nodes/{interfaces, FigmaRestNodes, FigmaPluginNodes}
specs-from-figma/src/Runtime/Foundations/{interface, FigmaRest…, FigmaPlugin…}

specs-plugin-2 imports them from @specs-from-figma/Runtime/... instead of owning them. The plugin still owns its UI, controller, output rendering, and build pipeline.

This is safe given the project's constraints: specs-from-figma is private; plugin-only code leaking into the obfuscated package consumed by specs-cli is acceptable; and the plugin already builds directly from specs-from-figma source (tsconfig alias), so no new coupling is introduced.

Benefits

  • Both adapters sit next to the interface they implement — adding/changing an interface method makes the missing-impl divergence obvious.
  • specs-from-figma's test suite can (and should) exercise the plugin-shaped path, closing the "green in engine, broken in plugin" gap.
  • The plugin adapters become first-class — the current @ts-expect-error reach-ins into specs-from-figma privates go away.

Scope / acceptance

  • Move FigmaPluginNodes + FigmaPluginFoundations into specs-from-figma, carrying their current (latest) logic (note: these files have changes on the examples-slots-instances branch not yet in release — preserve those).
  • Rewire specs-plugin-2 imports; delete the @ts-expect-error reach-ins.
  • Add @figma/plugin-typings as a specs-from-figma devDependency.
  • Keep figma.* references inside methods only (never module top-level) so the CLI/REST build can bundle the plugin adapter without evaluating figma. Preserve the node-builtin-stub invariant (build-plugin.js stubs node:crypto/etc).
  • Engine traversal tests run against both a REST-shaped and a plugin-shaped fixture.

Stretch (the deeper fix)

Once both adapters live together, route raw node-property access in plane-2 through the adapter (e.g. componentId(node), children(node), type(node)) so traversal code never does (child as any).componentId again — or normalize nodes into a canonical internal tree at the runtime boundary.

Notes

  • Larger refactor, intentionally scoped outside the examples feature work (ADR-047/048/050).
  • To be done on a branch off the release branch; reconcile with the adapter changes currently on examples-slots-instances.

Metadata

Metadata

Assignees

Labels

generatorspecs-from-figma processing enginepluginFigma plugin

Type

No fields configured for Task.

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions