Files
exact/scripts/executable-name.test.mjs
Eliot HertensteinandClaude Opus 5.5 2b0e5af12b An app named like one of its bundle's own entries builds, its executable suffixed " App" (#467)
* An app named like one of its bundle's own entries builds, its executable suffixed " App"

The executable is named after the app and sits at an iOS bundle's root,
beside Info.plist, Frameworks, PlugIns, assets and the rest: an app called
"Assets" or "Frameworks" failed at the end of packaging with an
unrelated error, and the build service refuses such names up front
(expo/exact-build#89). executableName now adds " App" when the name would
match one of those entries, case-insensitively, so the app builds; its
display name is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* executableName's reserved list fits app.mjs's line cap

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Only an iOS or tvOS executable takes the " App" suffix; a Mac or Windows one keeps the app's name

Fable and Astra: the suffix was in executableName, which every platform
shares, so a Mac app named "Plans" or "Watch" would have become
"Plans App.app", installed beside its old bundle, missed by uninstall and
the DMG; Windows likewise. The collision exists only at a flat bundle's
root. executableName is as before; appleExecutableName(app, true) adds the
suffix, and only the iOS/tvOS paths ask for it: appleArtifacts for a
non-Mac destination, the iOS Info.plist, install, agent-ios and the App
Clip. The reserved set also takes PrivacyInfo.xcprivacy and ExactResources,
which iosExecutableRefusal would otherwise still refuse, and the test ties
it to APPLE_STATIC_TREES.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-13 03:25:14 -08:00

19 lines
1.3 KiB
JavaScript

import { expect, test } from 'bun:test';
import { APPLE_STATIC_TREES } from '../host/apple/assets.mjs';
import { appleExecutableName, executableName } from './app.mjs';
// An iOS app named like one of its bundle root's own entries builds, under an executable that does not collide
// with it; a Mac or Windows executable, which shares no folder with them, keeps the app's name (expo/exact-build#89).
test("only a flat bundle's executable that would name one of the root's entries gets an App suffix", () => {
const app = (displayName) => ({ displayName, id: 'com.example.x' });
expect(appleExecutableName(app('Notes'), true)).toBe('Notes');
for (const name of ['Assets', 'assets', 'Frameworks', 'PlugIns', 'Info.plist', 'PrivacyInfo.xcprivacy', 'ExactResources', 'Watch', 'en.lproj', '[email protected]']) {
expect(appleExecutableName(app(name), true)).toBe(`${name} App`);
expect(appleExecutableName(app(name))).toBe(name);
expect(executableName(app(name))).toBe(name);
}
// Every tree the build copies to the bundle root is reserved, whatever its case.
for (const tree of APPLE_STATIC_TREES) expect(appleExecutableName(app(tree.toUpperCase()), true)).toBe(`${tree.toUpperCase()} App`);
expect(appleExecutableName(app('Café'), true)).toBe('Café'.normalize('NFD'));
});