UNPKG

icon-numic-plugin

Version:

Generate icons for Android and iOS in React Native.

273 lines (269 loc) 10.1 kB
// 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