apple-targets-hugo-patch
Version:
Generate Apple Targets with Expo Prebuild
254 lines (253 loc) • 11 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateWatchIconsInternalAsync = exports.generateIconsInternalAsync = exports.generateResizedImageAsync = exports.setIconsAsync = exports.ICON_CONTENTS = exports.withImageAsset = void 0;
const config_plugins_1 = require("@expo/config-plugins");
const image_utils_1 = require("@expo/image-utils");
const AssetContents_1 = require("@expo/prebuild-config/build/plugins/icons/AssetContents");
const fs = __importStar(require("fs"));
const path_1 = __importStar(require("path"));
const withImageAsset = (config, { cwd, name, image }) => {
return (0, config_plugins_1.withDangerousMod)(config, [
"ios",
async (config) => {
const projectRoot = config.modRequest.projectRoot;
const iosNamedProjectRoot = (0, path_1.join)(projectRoot, cwd);
const imgPath = `Assets.xcassets/${name}.imageset`;
// Ensure the Images.xcassets/AppIcon.appiconset path exists
await fs.promises.mkdir((0, path_1.join)(iosNamedProjectRoot, imgPath), {
recursive: true,
});
const userDefinedIcon = typeof image === "string"
? { "1x": image, "2x": undefined, "3x": undefined }
: image;
// Finally, write the Config.json
await (0, AssetContents_1.writeContentsJsonAsync)((0, path_1.join)(iosNamedProjectRoot, imgPath), {
images: await generateResizedImageAsync(Object.fromEntries(Object.entries(userDefinedIcon).map(([key, value]) => [
key,
(value === null || value === void 0 ? void 0 : value.match(/^[./]/)) ? path_1.default.join(cwd, value) : value,
])), name, projectRoot, iosNamedProjectRoot, path_1.default.join(cwd, "gen-image", name)),
});
return config;
},
]);
};
exports.withImageAsset = withImageAsset;
const IMAGE_CACHE_NAME = "widget-icons-";
const IMAGESET_PATH = "Assets.xcassets/AppIcon.appiconset";
// Hard-coding seemed like the clearest and safest way to implement the sizes.
exports.ICON_CONTENTS = [
{
idiom: "iphone",
sizes: [
{
size: 20,
scales: [2, 3],
},
{
size: 29,
scales: [1, 2, 3],
},
{
size: 40,
scales: [2, 3],
},
{
size: 60,
scales: [2, 3],
},
// TODO: 76x76@2x seems unused now
// {
// size: 76,
// scales: [2],
// },
],
},
{
idiom: "ipad",
sizes: [
{
size: 20,
scales: [1, 2],
},
{
size: 29,
scales: [1, 2],
},
{
size: 40,
scales: [1, 2],
},
{
size: 76,
scales: [1, 2],
},
{
size: 83.5,
scales: [2],
},
],
},
{
idiom: "ios-marketing",
sizes: [
{
size: 1024,
scales: [1],
},
],
},
];
async function setIconsAsync(icon, projectRoot, iosNamedProjectRoot, cacheComponent) {
// Ensure the Images.xcassets/AppIcon.appiconset path exists
await fs.promises.mkdir((0, path_1.join)(iosNamedProjectRoot, IMAGESET_PATH), {
recursive: true,
});
// Finally, write the Config.json
await (0, AssetContents_1.writeContentsJsonAsync)((0, path_1.join)(iosNamedProjectRoot, IMAGESET_PATH), {
images: await generateIconsInternalAsync(icon, projectRoot, iosNamedProjectRoot, cacheComponent),
});
}
exports.setIconsAsync = setIconsAsync;
async function generateResizedImageAsync(icon, name, projectRoot, iosNamedProjectRoot, cacheComponent) {
// Store the image JSON data for assigning via the Contents.json
const imagesJson = [];
// If the user provided a single image, then assume it's the 3x image and generate the 1x and 2x images.
// const shouldResize = typeof icon === "string";
const userDefinedIcon = typeof icon === "string"
? { "1x": icon, "2x": undefined, "3x": undefined }
: icon;
for (const icon of Object.entries(userDefinedIcon)) {
const [scale, iconPath] = icon;
const filename = `${scale}.png`;
const imgEntry = {
idiom: "universal",
// @ts-ignore: template types not supported in TS yet
scale,
};
if (iconPath) {
// Using this method will cache the images in `.expo` based on the properties used to generate them.
// this method also supports remote URLs and using the global sharp instance.
const { source } = await (0, image_utils_1.generateImageAsync)({ projectRoot, cacheType: IMAGE_CACHE_NAME + cacheComponent },
// @ts-expect-error
{
src: iconPath,
name: filename,
});
// Write image buffer to the file system.
const assetPath = (0, path_1.join)(iosNamedProjectRoot, `Assets.xcassets/${name}.imageset`, filename);
await fs.promises.writeFile(assetPath, source);
if (filename) {
imgEntry.filename = filename;
}
}
imagesJson.push(imgEntry);
}
return imagesJson;
}
exports.generateResizedImageAsync = generateResizedImageAsync;
async function generateIconsInternalAsync(icon, projectRoot, iosNamedProjectRoot, cacheComponent) {
// Store the image JSON data for assigning via the Contents.json
const imagesJson = [];
// keep track of icons that have been generated so we can reuse them in the Contents.json
const generatedIcons = {};
for (const platform of exports.ICON_CONTENTS) {
const isMarketing = platform.idiom === "ios-marketing";
for (const { size, scales } of platform.sizes) {
for (const scale of scales) {
// The marketing icon is special because it makes no sense.
const filename = isMarketing
? "ItunesArtwork@2x.png"
: getAppleIconName(size, scale);
// Only create an image that hasn't already been generated.
if (!(filename in generatedIcons)) {
const iconSizePx = size * scale;
// Using this method will cache the images in `.expo` based on the properties used to generate them.
// this method also supports remote URLs and using the global sharp instance.
const { source } = await (0, image_utils_1.generateImageAsync)({ projectRoot, cacheType: IMAGE_CACHE_NAME + cacheComponent }, {
src: icon,
name: filename,
width: iconSizePx,
height: iconSizePx,
removeTransparency: true,
// The icon should be square, but if it's not then it will be cropped.
resizeMode: "cover",
// Force the background color to solid white to prevent any transparency.
// TODO: Maybe use a more adaptive option based on the icon color?
backgroundColor: "#ffffff",
});
// Write image buffer to the file system.
const assetPath = (0, path_1.join)(iosNamedProjectRoot, IMAGESET_PATH, filename);
await fs.promises.writeFile(assetPath, source);
// Save a reference to the generated image so we don't create a duplicate.
generatedIcons[filename] = true;
}
imagesJson.push({
idiom: platform.idiom,
size: `${size}x${size}`,
// @ts-ignore: template types not supported in TS yet
scale: `${scale}x`,
filename,
});
}
}
}
return imagesJson;
}
exports.generateIconsInternalAsync = generateIconsInternalAsync;
async function generateWatchIconsInternalAsync(icon, projectRoot, iosNamedProjectRoot, cacheComponent) {
// Store the image JSON data for assigning via the Contents.json
const imagesJson = [];
const size = 1024;
const filename = getAppleIconName(size, 1);
// Using this method will cache the images in `.expo` based on the properties used to generate them.
// this method also supports remote URLs and using the global sharp instance.
const { source } = await (0, image_utils_1.generateImageAsync)({ projectRoot, cacheType: IMAGE_CACHE_NAME + cacheComponent }, {
src: icon,
name: filename,
width: size,
height: size,
removeTransparency: true,
// The icon should be square, but if it's not then it will be cropped.
resizeMode: "cover",
// Force the background color to solid white to prevent any transparency.
// TODO: Maybe use a more adaptive option based on the icon color?
backgroundColor: "#ffffff",
});
// Write image buffer to the file system.
const assetPath = (0, path_1.join)(iosNamedProjectRoot, IMAGESET_PATH, filename);
await fs.promises.writeFile(assetPath, source);
imagesJson.push({
filename: getAppleIconName(size, 1),
idiom: "universal",
size: `${size}x${size}`,
platform: "watchos",
});
return imagesJson;
}
exports.generateWatchIconsInternalAsync = generateWatchIconsInternalAsync;
function getAppleIconName(size, scale) {
return `App-Icon-${size}x${size}@${scale}x.png`;
}