Skip to content

Commit 5bed168

Browse files
jaggujiclaude
andcommitted
feat: resolve Button/Buttons Figma naming gap via alias file, not caller-side normalization
CodeConnectRegistry.resolve("Buttons", ...) returned None even though the mapping exists under "Button" -- blend-design-system's figma.connect() call uses the code identifier "Button", but the real published Figma component set is named "Buttons" (plural, confirmed via Figma Desktop's Inspect panel). figma/figmaComponentAliases.mjs records verified Figma-display-name aliases and generate-figma-code-connect.mjs folds them into extra resolve patterns, so a consumer never has to hand-normalize Figma names itself. Kept separate from figma/componentMaps/*.mjs so figma:sync never overwrites it (sync only knows blend's code identifier, not Figma's display name). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 4fbe52f commit 5bed168

4 files changed

Lines changed: 76 additions & 40 deletions

File tree

figma/README.md

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Add `@juspay/rescript-blend` as a dependency, then call `toProps` directly
196196
wrapper/state composition exactly as it already is:
197197

198198
```rescript
199-
| "Buttons" =>
199+
| "Button" =>
200200
(JuspayRescriptBlend.ButtonCodeConnect.toProps(componentProps), "Blend.Button", "")
201201
->convertPropsNodeStateVariable("")
202202
```
@@ -225,7 +225,7 @@ nested icon instances.
225225

226226
### CodeConnectRegistry: no per-component code at all
227227

228-
The `| "Buttons" => ...` switch above still has to be hand-written per
228+
The `| "Button" => ...` switch above still has to be hand-written per
229229
component, one arm per synced map -- the same duplication `toProps`
230230
already removed for property mappings, one level up for components.
231231
`src/Figma/CodeConnectRegistry.res` (generated alongside every other
@@ -246,19 +246,27 @@ edits -- no new switch arm to add. `resolve` returns
246246
still supplies its own tag prefix (`"Blend." ++ codeComponent` above) and
247247
composition, exactly like `~tagName` on `fromFigmaProps`.
248248

249-
**Important naming caveat**, spelled out in the generated file's header
250-
too: `figmaComponentName` is whatever identifier blend-design-system's own
251-
`.figma.tsx` uses in its `figma.connect(<ComponentName>, ...)` call -- **not
252-
necessarily the literal name shown in Figma's layers/Inspect panel.** These
253-
can differ: Button's real Figma component set is named **"Buttons"**
254-
(plural, confirmed via Figma Desktop's Inspect panel), while blend's
255-
`figma.connect()` call uses the React identifier **"Button"** (singular) --
256-
so `resolve("Buttons", ...)` returns `None` even though a real mapping
257-
exists under the key `"Button"`. Reconciling a live Figma node's actual
258-
component name against this registry's keys (e.g. juspay-portal's existing
259-
`^ComponentName \d+$` instance-suffix normalization would need extending to
260-
handle plural/singular or other divergent names) is the caller's problem --
261-
this package only supplies the mapping, not Figma-node-name resolution.
249+
**Naming caveat, and how it's handled:** `figmaComponentName` is whatever
250+
identifier blend-design-system's own `.figma.tsx` uses in its
251+
`figma.connect(<ComponentName>, ...)` call -- **not necessarily the literal
252+
name shown in Figma's layers/Inspect panel.** These can differ: Button's
253+
real Figma component set is named **"Buttons"** (plural, confirmed via
254+
Figma Desktop's Inspect panel), while blend's `figma.connect()` call uses
255+
the React identifier **"Button"** (singular).
256+
257+
Rather than push that reconciliation onto every caller, it's handled here:
258+
`figma/figmaComponentAliases.mjs` records known Figma-display-name ↔
259+
code-identifier divergences (each one verified against the real Figma
260+
component, never guessed), and `scripts/generate-figma-code-connect.mjs`
261+
folds them into extra `resolve` patterns automatically --
262+
`resolve("Buttons", ...)` and `resolve("Button", ...)` both hit the same
263+
arm. This file is separate from `figma/componentMaps/*.mjs` specifically so
264+
`figma:sync` never overwrites it (sync only knows blend's code identifier,
265+
never Figma's own display name). A caller can pass a raw Figma
266+
component/layer name straight through with zero normalization of its own;
267+
if `resolve` returns `None` for a name you know is really one of the synced
268+
components, the fix is a new entry in `figmaComponentAliases.mjs` here, not
269+
a normalization step in the caller.
262270

263271
Scope: prop-mapped components only, same as `toProps`/`fromFigmaProps` --
264272
anything that needs to walk children (Modal/Table/Snackbar-style

figma/figmaComponentAliases.mjs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Manually-curated aliases: additional raw Figma component/component-set
2+
// names that should resolve to the same CodeConnectRegistry entry as the
3+
// figmaComponentName already synced into figma/componentMaps/<Component>.mjs.
4+
//
5+
// This file is NOT touched by scripts/sync-figma-code-connect.mjs and never
6+
// will be -- that script only knows the identifier blend-design-system's own
7+
// figma.connect(<Identifier>, ...) call uses (e.g. "Button"), which is a code
8+
// identifier, not necessarily the literal name Figma's own Inspect panel
9+
// shows for that component/component-set. Where the two differ, record it
10+
// here so a caller never has to hand-normalize the raw Figma name itself
11+
// before calling CodeConnectRegistry.resolve -- keeping that knowledge here
12+
// (and re-synced/regenerated automatically into the registry) is what lets
13+
// consumers avoid writing or maintaining any mapping of their own.
14+
//
15+
// Each entry must be verified against the real Figma component (Figma
16+
// Desktop's Inspect panel, or the Figma REST API) -- not guessed. An
17+
// unverified alias risks silently misrouting a real Figma layer name that
18+
// happens to coincide with blend's own naming.
19+
//
20+
// Keyed by componentName (the figma/componentMaps/<componentName>.mjs
21+
// filename, e.g. "Button"), value is an array of extra raw Figma names.
22+
23+
export default {
24+
// blend-design-system's Button.figma.tsx calls figma.connect(Button, ...)
25+
// -- the code identifier is "Button" (singular). The actual published
26+
// Figma component set is named "Buttons" (plural), confirmed via Figma
27+
// Desktop's Inspect panel on the real published component.
28+
Button: ["Buttons"],
29+
};

scripts/generate-figma-code-connect.mjs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { dirname, join, basename } from "node:path";
3939
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
4040
const MAPS_DIR = join(ROOT, "figma", "componentMaps");
4141
const OUT_DIR = join(ROOT, "src", "Figma");
42+
const ALIASES_PATH = join(ROOT, "figma", "figmaComponentAliases.mjs");
4243

4344
function renderEnumExtraction(varName, spec) {
4445
const cases = Object.entries(spec.values)
@@ -180,7 +181,13 @@ let fromFigmaProps = (props: CodeConnectUtils.figmaProps, ~tagName: string="Jusp
180181
// removed for property mappings, one level up for components). One arm
181182
// per component actually generated -- an unverified scaffold never leaks
182183
// in, same gate as everywhere else.
183-
async function writeRegistry(generated) {
184+
//
185+
// `aliases` (from figma/figmaComponentAliases.mjs) supplies extra raw Figma
186+
// names that should match the same arm -- see that file for why this can't
187+
// be derived from blend-design-system's own *.figma.tsx (it only knows the
188+
// code identifier it was given, not necessarily the literal name Figma's
189+
// own Inspect panel shows for that component/component-set).
190+
async function writeRegistry(generated, aliases) {
184191
const registryPath = join(OUT_DIR, "CodeConnectRegistry.res");
185192
if (generated.length === 0) {
186193
if (existsSync(registryPath)) {
@@ -191,10 +198,11 @@ async function writeRegistry(generated) {
191198
}
192199

193200
const arms = generated
194-
.map(
195-
(r) =>
196-
` | "${resStringLiteral(r.figmaComponentName)}" => Some((${r.componentName}CodeConnect.toProps(props), "${resStringLiteral(r.codeComponent)}"))`,
197-
)
201+
.map((r) => {
202+
const names = [r.figmaComponentName, ...(aliases[r.componentName] ?? [])];
203+
const patterns = names.map((n) => `"${resStringLiteral(n)}"`).join(" | ");
204+
return ` | ${patterns} => Some((${r.componentName}CodeConnect.toProps(props), "${resStringLiteral(r.codeComponent)}"))`;
205+
})
198206
.join("\n");
199207

200208
const source = `// GENERATED by scripts/generate-figma-code-connect.mjs. Do not edit by hand.
@@ -206,15 +214,10 @@ async function writeRegistry(generated) {
206214
// IMPORTANT: figmaComponentName is whatever identifier blend-design-system's
207215
// own *.figma.tsx uses in its figma.connect(<ComponentName>, ...) call --
208216
// NOT necessarily the literal name shown in Figma's layers/Inspect panel.
209-
// These can differ: Button's real Figma component set is named "Buttons"
210-
// (plural, confirmed via Figma Desktop's Inspect panel), while blend's
211-
// figma.connect() call uses the React identifier "Button" (singular), so
212-
// this registry's "Button" key won't match a raw Figma layer name of
213-
// "Buttons" without the caller normalizing first (same idea as
214-
// juspay-portal's existing ^ComponentName \\d+$ instance-suffix
215-
// normalization). Resolving the actual Figma component name from a live
216-
// node, and reconciling it against this registry's keys, is the caller's
217-
// problem -- this package only supplies the mapping.
217+
// Where the two are known to differ (e.g. Button's real Figma component set
218+
// is named "Buttons", plural), figma/figmaComponentAliases.mjs supplies the
219+
// extra name(s) below, so the caller can still pass the raw Figma name
220+
// straight through with no normalization of its own.
218221
//
219222
// Returns Some((props, codeComponent)) for anything synced/verified, None
220223
// otherwise -- the caller supplies its own tag prefix/wrapper (e.g.
@@ -266,7 +269,8 @@ async function main() {
266269
}
267270
}
268271

269-
const registryResult = await writeRegistry(generated);
272+
const aliases = existsSync(ALIASES_PATH) ? (await import(ALIASES_PATH)).default : {};
273+
const registryResult = await writeRegistry(generated, aliases);
270274

271275
for (const r of generated) console.log(`wrote src/Figma/${r.componentName}CodeConnect.res`);
272276
for (const f of removed) console.log(`removed stale src/Figma/${f} (no longer a ready map)`);

src/Figma/CodeConnectRegistry.res

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,10 @@
77
// IMPORTANT: figmaComponentName is whatever identifier blend-design-system's
88
// own *.figma.tsx uses in its figma.connect(<ComponentName>, ...) call --
99
// NOT necessarily the literal name shown in Figma's layers/Inspect panel.
10-
// These can differ: Button's real Figma component set is named "Buttons"
11-
// (plural, confirmed via Figma Desktop's Inspect panel), while blend's
12-
// figma.connect() call uses the React identifier "Button" (singular), so
13-
// this registry's "Button" key won't match a raw Figma layer name of
14-
// "Buttons" without the caller normalizing first (same idea as
15-
// juspay-portal's existing ^ComponentName \d+$ instance-suffix
16-
// normalization). Resolving the actual Figma component name from a live
17-
// node, and reconciling it against this registry's keys, is the caller's
18-
// problem -- this package only supplies the mapping.
10+
// Where the two are known to differ (e.g. Button's real Figma component set
11+
// is named "Buttons", plural), figma/figmaComponentAliases.mjs supplies the
12+
// extra name(s) below, so the caller can still pass the raw Figma name
13+
// straight through with no normalization of its own.
1914
//
2015
// Returns Some((props, codeComponent)) for anything synced/verified, None
2116
// otherwise -- the caller supplies its own tag prefix/wrapper (e.g.
@@ -27,7 +22,7 @@ let resolve = (figmaComponentName: string, props: CodeConnectUtils.figmaProps):
2722
)> =>
2823
switch figmaComponentName {
2924
| "AvatarGroup" => Some((AvatarGroupCodeConnect.toProps(props), "AvatarGroup"))
30-
| "Button" => Some((ButtonCodeConnect.toProps(props), "Button"))
25+
| "Button" | "Buttons" => Some((ButtonCodeConnect.toProps(props), "Button"))
3126
| "ButtonGroup" => Some((ButtonGroupCodeConnect.toProps(props), "ButtonGroup"))
3227
| "Charts" => Some((ChartsCodeConnect.toProps(props), "Charts"))
3328
| "DropdownInput" => Some((DropdownInputCodeConnect.toProps(props), "DropdownInput"))

0 commit comments

Comments
 (0)