diff --git a/CHANGELOG.md b/CHANGELOG.md index 77d3317bd..986f8ef90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Added +- Run scripts written for Figma's dynamic-page mode that call `figma.getNodeByIdAsync()` or `getMainComponentAsync()`; both resolve to the same nodes as their synchronous forms. - Export components to Storybook with `openpencil export -f storybook`: one CSF3 story file per component set or component for React, Vue, or HTML, with a story and `select` controls per variant, a design image per variant, and an `openpencil://` link that opens the variant in OpenPencil. `--watch` re-exports on every save and removes stories of deleted components (#727). - Export HTML and Tailwind JSX from the app's export options and through the IO registry, and export Tailwind JSX from the CLI with `-f tailwind-jsx` (`-f jsx --style tailwind` still works). HTML export of a single layer now includes the layer itself, as other formats do. - Choose PPTX in the Export panel's format list, alongside PNG, JPG, WEBP, SVG, and PDF. diff --git a/packages/core/src/figma-api/compatibility.ts b/packages/core/src/figma-api/compatibility.ts index e1cfb3545..dd6a8e356 100644 --- a/packages/core/src/figma-api/compatibility.ts +++ b/packages/core/src/figma-api/compatibility.ts @@ -70,6 +70,7 @@ type InstancePropertySurfaceMatch = Expect< | 'setProperties' | 'isExposedInstance' | 'exposedInstances' + | 'getMainComponentAsync' >, Pick< InstanceNode, @@ -78,6 +79,7 @@ type InstancePropertySurfaceMatch = Expect< | 'setProperties' | 'isExposedInstance' | 'exposedInstances' + | 'getMainComponentAsync' > > > diff --git a/packages/core/src/figma-api/index.ts b/packages/core/src/figma-api/index.ts index a9a8987f8..3706dd110 100644 --- a/packages/core/src/figma-api/index.ts +++ b/packages/core/src/figma-api/index.ts @@ -137,6 +137,11 @@ export class FigmaAPI implements NodeProxyHost { return node ? this.wrapNode(id) : null } + /** The async lookup that Figma requires in dynamic-page mode; same result as getNodeById. */ + async getNodeByIdAsync(id: string): Promise { + return this.getNodeById(id) + } + // --- Node Creation --- private _createNode(type: NodeType): FigmaNodeProxy { diff --git a/packages/core/src/figma-api/proxy.ts b/packages/core/src/figma-api/proxy.ts index 26813c9f1..641760c65 100644 --- a/packages/core/src/figma-api/proxy.ts +++ b/packages/core/src/figma-api/proxy.ts @@ -222,6 +222,11 @@ export class FigmaNodeProxy { setPageBackgrounds(this[INTERNAL_GRAPH], this._raw(), value) } + /** The async form Figma requires in dynamic-page mode; same result as mainComponent. */ + async getMainComponentAsync(): Promise { + return this.mainComponent + } + get mainComponent(): FigmaNodeProxy | null { const n = this._raw() if (!n.componentId) return null diff --git a/packages/docs/programmable/cli/scripting.md b/packages/docs/programmable/cli/scripting.md index 40d6802fc..e3c0f0478 100644 --- a/packages/docs/programmable/cli/scripting.md +++ b/packages/docs/programmable/cli/scripting.md @@ -162,6 +162,7 @@ Common node properties are readable/writable through the proxy, including: - `figma.createImage(data)` - `figma.loadFontAsync(fontName)` no-ops because OpenPencil does not gate text edits on plugin font loading - `figma.listAvailableFontsAsync()` returns host-provided fonts when available +- `figma.getNodeByIdAsync(id)` and `instance.getMainComponentAsync()` resolve to the same nodes as `figma.getNodeById(id)` and `instance.mainComponent`, for scripts written for Figma's dynamic-page mode - `figma.notify(message)` logs a warning in headless mode - `figma.viewport` diff --git a/tests/engine/figma/api/components.test.ts b/tests/engine/figma/api/components.test.ts index 038a8a03f..4b22235ac 100644 --- a/tests/engine/figma/api/components.test.ts +++ b/tests/engine/figma/api/components.test.ts @@ -61,4 +61,15 @@ describe('components', () => { expect(instance.type).toBe('INSTANCE') expect(expectDefined(instance.mainComponent, 'instance main component').id).toBe(comp.id) }) + + test('async lookups resolve like their synchronous forms', async () => { + const api = createAPI() + const component = api.createComponent() + const instance = component.createInstance() + + expect((await api.getNodeByIdAsync(instance.id))?.id).toBe(instance.id) + expect(await api.getNodeByIdAsync('0:404')).toBeNull() + expect((await instance.getMainComponentAsync())?.id).toBe(component.id) + expect(await component.getMainComponentAsync()).toBeNull() + }) })