| 571 | * Generates app-icons for specified option. |
| 572 | */ |
| 573 | export async function generateAppIcons(option: `${Platforms}` | "all") { |
| 574 | const { path, exists, find, copy, write } = filesystem |
| 575 | const cwd = process.cwd() |
| 576 | |
| 577 | const options = option === "all" ? Object.values(Platforms) : ([option] as `${Platforms}`[]) |
| 578 | |
| 579 | const optionGenerationSuccesses = [] |
| 580 | |
| 581 | // start the generation process for each platform |
| 582 | // looping instead of mapping allows us to await for each platform sequentially |
| 583 | for (const o of options) { |
| 584 | const optionProjectName = { expo: "Expo" }[o] |
| 585 | |
| 586 | // find the output path for platform and check if it exists |
| 587 | // iOS is a bit weird since it's named differently for each project |
| 588 | const relativeOutputDirPath = { |
| 589 | expo: "assets/images", |
| 590 | android: "android/app/src/main/res", |
| 591 | ios: (function () { |
| 592 | const searchPath = path(cwd, "ios") |
| 593 | |
| 594 | if (!exists(searchPath)) return searchPath |
| 595 | |
| 596 | return ( |
| 597 | find(searchPath, { |
| 598 | directories: true, |
| 599 | files: false, |
| 600 | matching: "AppIcon.appiconset", |
| 601 | })?.[0] || "ios/**/Images.xcassets/AppIcon.appiconset" |
| 602 | ) |
| 603 | })(), |
| 604 | }[o] |
| 605 | const outputDirPath = path(cwd, relativeOutputDirPath) |
| 606 | |
| 607 | // if not, skip... |
| 608 | if (exists(outputDirPath) !== "dir") { |
| 609 | warning( |
| 610 | `⚠️ No output directory found for "${optionProjectName}" at "${outputDirPath}". Skipping...`, |
| 611 | ) |
| 612 | continue |
| 613 | } |
| 614 | |
| 615 | heading(`Generating ${optionProjectName} app icons...`) |
| 616 | |
| 617 | const icons = APP_ICON_RULESET.icons.filter((i) => i.platform === o) |
| 618 | |
| 619 | // prepare each icon for generation sequentially |
| 620 | for (const i of icons) { |
| 621 | const inputFilePath = path(templatesDir(), "app-icon", i.inputFile) |
| 622 | |
| 623 | // get the input file for sharp and do some initial transforms when necessary (e.g. radius and padding) |
| 624 | const inputFile = await (async function () { |
| 625 | if (!i.transform) return inputFilePath |
| 626 | |
| 627 | try { |
| 628 | const { size, radius, padding } = i.transform |
| 629 | const cutoutMask = Buffer.from( |
| 630 | `<svg><rect x="0" y="0" width="${size}" height="${size}" rx="${radius}" ry="${radius}"/></svg>`, |