@compare-ui/figma
v0.1.0
Published
Figma resource resolver for design-comparison workflows.
Readme
@compare-ui/figma
Figma resource resolver for design-comparison workflows.
This package adds Figma as a supported image source by registering a resolver with @compare-ui/core.
Installation
pnpm add @compare-ui/figmaIf you plan to author shared comparison config directly with core helpers, also install:
pnpm add @compare-ui/coreIf you plan to execute comparison runs through the shared runner, also install:
pnpm add @compare-ui/runnerWhat it exports
Main public APIs:
createFigmaImageResourceResolver(...)registerFigmaImageResourceResolver(...)registerFigmaFromEnv(...)
Main public types:
FigmaUrlResourceDefinitionFigmaNodeResourceDefinitionFigmaImageResourceDefinitionFigmaCacheOptionsFigmaResolverOptionsRegisterFigmaFromEnvOptions
Published entrypoint:
@compare-ui/figma
That entrypoint is intended to resolve from the published build output, and the workspace verifies it through a tarball smoke test.
Figma resource type
This package adds a Figma-backed resource type to the core resource model.
type CropOffsets = {
top?: number;
right?: number;
bottom?: number;
left?: number;
};
type FigmaUrlResourceDefinition = {
url: string;
crop?: CropOffsets;
};
type FigmaNodeResourceDefinition = {
fileKey: string;
nodeId: string;
format?: 'png';
scale?: number;
crop?: CropOffsets;
};
type FigmaImageResourceDefinition = FigmaUrlResourceDefinition | FigmaNodeResourceDefinition;That allows configs to use either form.
Using a full Figma URL:
reference: {
type: 'figma',
url: 'https://figma.com/design/abc123/Example?node-id=2020-1617',
crop: {
bottom: 20
}
}Using fileKey and nodeId directly:
reference: {
type: 'figma',
fileKey: 'abc123',
nodeId: '2020:1617',
crop: {
bottom: 20
}
}Choose the form that fits your workflow:
- use
urlwhen you already have a copied Figma link - use
fileKeyandnodeIdwhen those values already exist in your integration flow
The url form is intended for copied Figma design links that include a node-id query parameter.
How the figma type becomes available
This package handles the type registration for you.
When @compare-ui/figma is part of your project, it augments DesignComparisonResourceMap from @compare-ui/core, so TypeScript understands that type: 'figma' is a valid resource.
Conceptually, this package adds a type definition like this:
declare module '@compare-ui/core' {
interface DesignComparisonResourceMap {
figma: FigmaImageResourceDefinition;
}
}As a consumer of this package, you do not need to write that augmentation yourself.
TypeScript still needs to see the @compare-ui/figma module in your program. If your editor or build does not recognize type: 'figma' yet, add a type-loading import in a setup module that is already included by TypeScript:
import '@compare-ui/figma';Common places for that import:
- a shared test setup file
- a
types.tsmodule already included by the app - the same bootstrap module where you register the resolver
That import enables the type augmentation. Resolver registration is still a separate runtime step.
TypeScript setup
Use this two-step model when adding Figma resources:
- Make the type augmentation visible to TypeScript.
- Register the resolver at runtime.
Example:
import '@compare-ui/figma';
import { registerFigmaFromEnv } from '@compare-ui/figma';
registerFigmaFromEnv();If the type-loading import is missing from the program, type: 'figma' may still fail to type-check even when the package is installed correctly.
This package owns that type-loading model. Other package docs may mirror the recipe, but they should not redefine it.
Registering the resolver
Register the resolver once during bootstrap.
Example:
import '@compare-ui/figma';
import { registerFigmaFromEnv } from '@compare-ui/figma';
registerFigmaFromEnv();After registration, any package that uses @compare-ui/core or @compare-ui/runner can resolve type: 'figma' resources.
If you need explicit token wiring or want all resolver options in one place, use:
createFigmaImageResourceResolver(...)registerFigmaImageResourceResolver(...)
Resolver options
Example shape:
type FigmaResolverOptions = {
accessToken: string;
maxRetries?: number;
retryDelayMs?: number;
cache?: boolean | FigmaCacheOptions;
};
type FigmaCacheOptions = {
/**
* Defaults to true.
*/
enabled?: boolean;
/**
* Defaults to ".desing-comp-cache" resolved from process.cwd().
*/
directory?: string;
};maxRetries and retryDelayMs apply bounded retry behavior to transient failures such as:
- network resets like
ECONNRESET - HTTP
429 - temporary
5xxresponses
Auth and validation failures are still treated as immediate errors.
If more options are needed later, they should extend this bootstrap configuration rather than changing story or fixture authoring formats.
Cache
The Figma resolver uses a local file cache by default.
The cache is there to avoid repeated Figma API calls and reduce the chance of hitting rate limits. It stores downloaded rendered PNG bytes under:
.desing-comp-cache/Default settings are equivalent to:
cache: {
enabled: true,
directory: '.desing-comp-cache',
}The cache directory is resolved from the current working directory, so repeated runs from the same project reuse the same Figma renders.
If cache is omitted, or if cache is provided without directory, the resolver uses .desing-comp-cache.
Cache entries are keyed by the normalized render request:
- Figma file key
- normalized node id
- format
- scale, when provided
Crop offsets are not part of the cache key because cropping is handled later by @compare-ui/core.
With default options, a second identical Figma resource should be served from .desing-comp-cache without calling the Figma image endpoint again.
For explicit registration, omit cache to keep the default cache on:
import { registerFigmaImageResourceResolver } from '@compare-ui/figma';
registerFigmaImageResourceResolver({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
});registerFigmaFromEnv() also keeps cache on by default.
Disable cache in code
Pass cache: false when creating or registering the resolver:
import { registerFigmaImageResourceResolver } from '@compare-ui/figma';
registerFigmaImageResourceResolver({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
cache: false,
});The same option is available on createFigmaImageResourceResolver(...) and registerFigmaFromEnv(...).
Use a custom cache directory
import { registerFigmaImageResourceResolver } from '@compare-ui/figma';
registerFigmaImageResourceResolver({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
cache: {
directory: './.cache/figma-renders',
},
});Basic usage
Example:
import { registerFigmaFromEnv } from '@compare-ui/figma';
import { runDesignComparisonCase } from '@compare-ui/runner';
registerFigmaFromEnv();
const result = await runDesignComparisonCase({
executionCase: {
name: 'home-screen',
reference: {
type: 'figma',
url: 'https://figma.com/design/abc123/Example?node-id=2020-1617',
crop: {
bottom: 20,
},
},
actual: {
type: 'fs',
path: './actual.png',
},
compare: {
threshold: 0.1,
acceptance: {
maxDiffPercent: 0.5,
},
},
},
output: {
outputFolder: './design-tests',
runId: crypto.randomUUID(),
},
});Typical usage
The expected workflow is:
- install or include
@compare-ui/figma - register the resolver during bootstrap
- use
type: 'figma'in config with eitherurlorfileKeyplusnodeId - run the normal comparison flow through higher-level packages
Notes
- This package adds Figma support on top of
@compare-ui/core. - Registration is explicit.
registerFigmaFromEnv()is the shortest happy-path runtime bootstrap.- Type augmentation is still a separate compile-time concern, so keep
import '@compare-ui/figma';somewhere in the program when TypeScript would not otherwise see the package. - The comparison flow still runs through the normal core and runner packages.
