perf(apple): state each NIB object's class, label, and ID once (#1477)

The NIBArchive projection synthesizes customClass, label, and objectID so
its records share the ibtool dictionary parser. The parser then returned
those values as class_name, name, and the object ID, and the graph node
kept the synthesized copies in attributes as well. Attributes now omit a
key whose value its canonical field already carries; differing authored
values stay. Transmission.app decode_interface_builder output shrinks
from 6.45 MB to 6.14 MB.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-10 00:31:00 -07:00 committed by GitHub
parent 2596f8da14
commit 5f6bb7a5ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 1 deletions

View File

@ -552,3 +552,32 @@ it("names the archive once per evidence record", () => {
evidence.filter(({ description }) => description.includes(relativePath)),
).toEqual([]);
});
it("states each object's class, label, and ID once", () => {
const { objects } = parseInterfaceBuilderRecords({
"com.apple.ibtool.document.objects": {
"7": {
customClass: "NSButton",
label: "Start",
objectID: "7",
nibValues: { NSContents: "Start" },
},
button: { class: "UIButton", title: "Build", objectID: "authored" },
},
});
expect(objects).toEqual([
expect.objectContaining({
id: "7",
class_name: "NSButton",
name: "Start",
attributes: { nibValues: { NSContents: "Start" } },
}),
expect.objectContaining({
id: "button",
class_name: "UIButton",
name: "Build",
// Values that differ from the canonical fields stay as authored.
attributes: { class: "UIButton", title: "Build", objectID: "authored" },
}),
]);
});

View File

@ -71,13 +71,23 @@ export const parseInterfaceBuilderRecords = (
attributes["name"],
className,
);
// class_name, name, and id already carry these values; repeating them in
// attributes doubled them on every graph node.
const carried = new Set<string>();
if (attributes["customClass"] === className) carried.add("customClass");
if (attributes["label"] === label) carried.add("label");
if (attributes["objectID"] === id) carried.add("objectID");
objects.push(
objectNode.parse({
id,
kind: classifyObject(className),
class_name: className,
name: label ?? id,
attributes: jsonSafeRecord(attributes),
attributes: jsonSafeRecord(
Object.fromEntries(
Object.entries(attributes).filter(([key]) => !carried.has(key)),
),
),
}),
);
}