icon-numic-plugin
Version:
Generate icons for Android and iOS in React Native.
273 lines (269 loc) • 10.1 kB
JavaScript
// index.ts
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
import { join as join2, dirname as dirname2 } from "path";
import sharp from "sharp";
// adaptive-icon.ts
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
import { dirname, join, extname } from "path";
import svg2vectordrawable from "svg2vectordrawable";
var androidXMLFiles = () => [
{
path: "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
contents: `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>`
},
{
path: "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
contents: `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>`
}
];
var solidBackgroundVector = (color) => `<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#FF${color}"
android:pathData="M0 0h108v108H0z"/>
</vector>`;
var convertSVG = (svgContents) => svg2vectordrawable(svgContents, { xmlTag: true });
var getFileType = (fileName) => extname(fileName ?? "").replace(".", "");
var writeResFile = (nativePath, path, contents) => {
const destinationFile = join(nativePath, "android/app/src/main/res", path);
const directory = dirname(destinationFile);
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true });
}
writeFileSync(destinationFile, contents);
};
var generateAndroidAdaptiveIcons = async (nativePath, projectPath, options, log) => {
const xmlFiles = androidXMLFiles();
if (!options.androidForeground || !options.androidBackground && !options.androidBackgroundColor) {
log("Not creating adaptive icons for Android");
return;
}
const foregroundType = getFileType(options.androidForeground);
const backgroundType = !options.androidBackgroundColor && getFileType(options.androidBackground);
if (foregroundType !== "svg" && foregroundType !== "xml") {
log('"androidForeground" invalid, .svg or .xml file required');
}
if (!options.androidBackgroundColor && backgroundType !== "svg" && backgroundType !== "xml") {
log('"androidBackground" invalid, .svg or .xml file required');
}
xmlFiles.forEach((file) => {
const destinationFile = join(nativePath, file.path);
const directory = dirname(destinationFile);
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true });
}
writeFileSync(destinationFile, file.contents);
});
if (foregroundType === "svg") {
const foregroundSVGContents = readFileSync(
join(projectPath, options.androidForeground),
"utf-8"
);
const foregroundVector = await convertSVG(foregroundSVGContents);
writeResFile(nativePath, "drawable-v24/ic_launcher_foreground.xml", foregroundVector);
}
if (foregroundType === "xml") {
const foregroundXMLContents = readFileSync(
join(projectPath, options.androidForeground),
"utf-8"
);
writeResFile(nativePath, "drawable-v24/ic_launcher_foreground.xml", foregroundXMLContents);
}
if (!options.androidBackgroundColor && backgroundType === "svg") {
const backgroundSVGContents = readFileSync(
join(projectPath, options.androidBackground),
"utf-8"
);
const backgroundVector = await convertSVG(backgroundSVGContents);
writeResFile(nativePath, "drawable/ic_launcher_background.xml", backgroundVector);
}
if (!options.androidBackgroundColor && backgroundType === "xml") {
const backgroundXMLContents = readFileSync(
join(projectPath, options.androidBackground),
"utf-8"
);
writeResFile(nativePath, "drawable/ic_launcher_background.xml", backgroundXMLContents);
}
if (options.androidBackgroundColor) {
const color = options.androidBackgroundColor.replace("#", "");
writeResFile(nativePath, "drawable/ic_launcher_background.xml", solidBackgroundVector(color));
}
};
// ios.ts
var contentsWithLinks = {
images: [
{
filename: "Icon-40.png",
idiom: "iphone",
scale: "2x",
size: "20x20"
},
{
filename: "Icon-60.png",
idiom: "iphone",
scale: "3x",
size: "20x20"
},
{
filename: "Icon-58.png",
idiom: "iphone",
scale: "2x",
size: "29x29"
},
{
filename: "Icon-87.png",
idiom: "iphone",
scale: "3x",
size: "29x29"
},
{
filename: "Icon-80.png",
idiom: "iphone",
scale: "2x",
size: "40x40"
},
{
filename: "Icon-120.png",
idiom: "iphone",
scale: "3x",
size: "40x40"
},
{
filename: "Icon-120.png",
idiom: "iphone",
scale: "2x",
size: "60x60"
},
{
filename: "Icon-180.png",
idiom: "iphone",
scale: "3x",
size: "60x60"
},
{
filename: "Icon-1024.png",
idiom: "ios-marketing",
scale: "1x",
size: "1024x1024"
}
],
info: {
author: "xcode",
version: 1
}
};
// index.ts
var iconSourcePaths = (projectPath) => [
join2(projectPath, "icon.png"),
join2(projectPath, "app-icon.png"),
join2(projectPath, "asset/icon.png"),
join2(projectPath, "logo.png"),
join2(projectPath, "icon.svg"),
join2(projectPath, "app-icon.svg"),
join2(projectPath, "asset/icon.svg"),
join2(projectPath, "logo.svg")
];
var getInput = (projectPath, options) => {
if (typeof options === "object" && typeof options.icon === "string" && existsSync2(join2(projectPath, options.icon))) {
return join2(projectPath, options.icon);
}
const paths = iconSourcePaths(projectPath);
let match;
paths.forEach((path) => {
if (!match && existsSync2(path)) {
match = path;
}
});
return match;
};
var getAndroidFolders = () => [
{ path: "android/app/src/main/res/mipmap-mdpi/ic_launcher.png", size: 48 },
{ path: "android/app/src/main/res/mipmap-hdpi/ic_launcher.png", size: 72 },
{ path: "android/app/src/main/res/mipmap-xhdpi/ic_launcher.png", size: 96 },
{ path: "android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png", size: 144 },
{ path: "android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png", size: 192 },
// Round icons.
{ path: "android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png", size: 48, round: true },
{ path: "android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png", size: 72, round: true },
{ path: "android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png", size: 96, round: true },
{ path: "android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png", size: 144, round: true },
{ path: "android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png", size: 192, round: true }
];
var getIOSFolders = (iosImageDirectory) => {
if (!iosImageDirectory) {
return [];
}
return [
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-40.png`, size: 40 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-58.png`, size: 58 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-60.png`, size: 60 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-80.png`, size: 80 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-87.png`, size: 87 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-120.png`, size: 120 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-180.png`, size: 180 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-1024.png`, size: 1024 }
];
};
var getSizes = (nativePath, log) => {
const iosDirectories = readdirSync(join2(nativePath, "ios"), { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).filter((dirent) => existsSync2(join2(nativePath, "ios", dirent.name, "Images.xcassets"))).map((dirent) => dirent.name);
const iosImageDirectory = iosDirectories.length > 0 ? join2("ios", iosDirectories[0], "Images.xcassets") : void 0;
if (!iosImageDirectory) {
log('iOS project directory with "Images.xcassets" not found', "warning");
}
return {
android: getAndroidFolders(),
ios: getIOSFolders(iosImageDirectory),
iosDirectory: iosImageDirectory
};
};
var icon_numic_plugin_default = async ({
projectPath = process.cwd(),
nativePath = process.cwd(),
// eslint-disable-next-line no-console
log = console.log,
options = {}
}) => {
const inputFile = getInput(projectPath, options);
const sizes = getSizes(nativePath, log);
const androidPromises = sizes.android.map((icon) => {
const destinationFile = join2(nativePath, icon.path);
const directory = dirname2(destinationFile);
if (!existsSync2(directory)) {
mkdirSync2(directory, { recursive: true });
}
return sharp(inputFile).resize(icon.size, icon.size).toFile(destinationFile);
});
await Promise.all(androidPromises);
await generateAndroidAdaptiveIcons(nativePath, projectPath, options, log);
const iosPromises = sizes.ios.map((icon) => {
const destinationFile = join2(nativePath, icon.path);
const directory = dirname2(destinationFile);
if (!existsSync2(directory)) {
mkdirSync2(directory, { recursive: true });
}
return sharp(inputFile).flatten({ background: options.iOSBackground ?? "#FFFFFF" }).resize(icon.size, icon.size).toFile(destinationFile);
});
await Promise.all(iosPromises);
if (sizes.iosDirectory) {
writeFileSync2(
join2(nativePath, sizes.iosDirectory, "AppIcon.appiconset/Contents.json"),
JSON.stringify(contentsWithLinks, null, 2)
);
}
};
export {
icon_numic_plugin_default as default
};
//# sourceMappingURL=index.js.map