UNPKG

@ionic/cli-utils

Version:
299 lines (298 loc) 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const format_1 = require("@ionic/cli-framework/utils/format"); const streams_1 = require("@ionic/cli-framework/utils/streams"); const utils_fs_1 = require("@ionic/utils-fs"); const chalk_1 = require("chalk"); const Debug = require("debug"); const fs = require("fs"); const path = require("path"); const http_1 = require("../../http"); const http_2 = require("../../utils/http"); const debug = Debug('ionic:cli-utils:lib:integrations:cordova:resources'); const SUPPORTED_SOURCE_EXTENSIONS = ['.psd', '.ai', '.png']; const UPLOAD_URL = 'https://res.ionic.io/api/v1/upload'; const TRANSFORM_URL = 'https://res.ionic.io/api/v1/transform'; function getImageResources(projectDir) { const images = []; for (const platform in exports.RESOURCES) { const platformConfig = exports.RESOURCES[platform]; for (const imageType in platformConfig) { const imageTypeConfig = platformConfig[imageType]; for (const image of imageTypeConfig.images) { images.push({ platform, resType: imageType, dest: path.resolve(projectDir, 'resources', platform, imageType, image.name), name: image.name, width: image.width, height: image.height, density: image.density, orientation: image.orientation, nodeName: imageTypeConfig.nodeName, nodeAttributes: imageTypeConfig.nodeAttributes, }); } } } return images; } exports.getImageResources = getImageResources; /** * Create the destination directories for the provided image resources. */ function createImgDestinationDirectories(imgResources) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const buildDirPromises = imgResources .map(img => path.dirname(img.dest)) .filter((dir, i, dirNames) => dirNames.indexOf(dir) === i) .map(dir => utils_fs_1.mkdirp(dir)); return Promise.all(buildDirPromises); }); } exports.createImgDestinationDirectories = createImgDestinationDirectories; /** * Find all source images within the resources directory */ function getSourceImages(projectDir, buildPlatforms, resourceTypes) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const resourceDir = path.resolve(projectDir, 'resources'); // TODO: hard-coded const srcDirList = buildPlatforms .map(platform => ({ platform, path: path.resolve(resourceDir, platform) })) .concat({ platform: 'global', path: resourceDir }); const sourceImages = []; for (const srcImgDir of srcDirList) { const srcImageDirContents = yield utils_fs_1.readDirSafe(srcImgDir.path); for (const srcImage of srcImageDirContents) { const ext = path.extname(srcImage); const resType = path.basename(srcImage, ext); if (SUPPORTED_SOURCE_EXTENSIONS.includes(ext) && resourceTypes.includes(resType)) { sourceImages.push({ ext, resType, platform: srcImgDir.platform, path: path.join(srcImgDir.path, srcImage), vector: false, height: 0, width: 0, }); } } } const sourceImageChecksums = yield Promise.all(sourceImages.map((img) => tslib_1.__awaiter(this, void 0, void 0, function* () { const [md5, cachedMd5] = yield utils_fs_1.getFileChecksums(img.path); if (cachedMd5) { debug(`${chalk_1.default.cyan('getSourceImages')}: ${chalk_1.default.bold(format_1.prettyPath(img.path))} cache md5: ${chalk_1.default.bold(cachedMd5)}`); img.cachedId = cachedMd5; } return md5; }))); return sourceImages.map((img, i) => (Object.assign({}, img, { imageId: sourceImageChecksums[i] }))); }); } exports.getSourceImages = getSourceImages; /** * Find the source image that matches the requirements of the image resource provided. */ function findMostSpecificSourceImage(imageResource, srcImagesAvailable) { return srcImagesAvailable.reduce((mostSpecificImage, sourceImage) => { if (sourceImage.platform === imageResource.platform && sourceImage.resType === imageResource.resType) { return sourceImage; } if (sourceImage.platform === 'global' && sourceImage.resType === imageResource.resType && !mostSpecificImage) { return sourceImage; } return mostSpecificImage; }, undefined); } exports.findMostSpecificSourceImage = findMostSpecificSourceImage; /** * Upload the provided source image through the resources web service. This will make it available * for transforms for the next 5 minutes. */ function uploadSourceImage(env, srcImage) { return tslib_1.__awaiter(this, void 0, void 0, function* () { let { req } = yield http_2.createRequest('POST', UPLOAD_URL, env.config.getHTTPConfig()); req = req .type('form') .attach('src', srcImage.path) .field('image_id', srcImage.imageId || ''); const res = yield req; return res.body; }); } exports.uploadSourceImage = uploadSourceImage; /** * Using the transformation web service, transform the provided image resource * into the appropriate w x h and then write the temporary file. */ function transformResourceImage(env, resource) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { req } = yield http_2.createRequest('POST', TRANSFORM_URL, env.config.getHTTPConfig()); const tmpDest = utils_fs_1.tmpfilepath(`ionic-cordova-resources-${resource.name}`); return new Promise(resolve => { let errorMarker = false; const result = { resource, tmpDest }; debug('creating write stream for tmp file: %s', tmpDest); const tmpfp = fs.createWriteStream(tmpDest) .on('error', (err) => { result.error = err; }) .on('finish', () => { if (!errorMarker) { resolve(result); } }); req .type('form') .send({ 'name': resource.name, 'image_id': resource.imageId, 'width': resource.width, 'height': resource.height, 'res_type': resource.resType, 'crop': 'center', 'encoding': 'png', }) .on('response', res => { debug('response %d received for %s: (id: %s)', res.statusCode, resource.name, resource.imageId); if (res.statusCode !== 200) { const generalErrorMsg = !res.statusCode || res.statusCode >= 500 ? 'the server may be experiencing difficulties right now--please try again later.' : 'the server marked a source image as invalid for this resource.'; errorMarker = true; const buf = new streams_1.WritableStreamBuffer(); res.pipe(buf); buf.on('finish', () => { let serverMsg = buf.consume().toString(); try { serverMsg = JSON.parse(serverMsg).Error; } catch (e) { serverMsg = http_1.formatResponseError(req, res.statusCode, serverMsg); } result.error = new Error(`Error while generating ${chalk_1.default.bold(resource.name)}: ` + `${generalErrorMsg}\n\n` + `Server: "${serverMsg}"`); resolve(result); }); } }) .on('error', err => { result.error = err; }) .pipe(tmpfp); }); }); } exports.transformResourceImage = transformResourceImage; /** * Add image resource references for the provided platforms to the project's config.xml file. */ function addResourcesToConfigXml(conf, platformList, resourceJson) { return tslib_1.__awaiter(this, void 0, void 0, function* () { for (const platform of platformList) { yield conf.ensurePlatformImages(platform, resourceJson[platform]); } yield conf.ensureSplashScreenPreferences(); }); } exports.addResourcesToConfigXml = addResourcesToConfigXml; exports.RESOURCES = { android: { icon: { images: [ { name: 'drawable-ldpi-icon.png', width: 36, height: 36, density: 'ldpi' }, { name: 'drawable-mdpi-icon.png', width: 48, height: 48, density: 'mdpi' }, { name: 'drawable-hdpi-icon.png', width: 72, height: 72, density: 'hdpi' }, { name: 'drawable-xhdpi-icon.png', width: 96, height: 96, density: 'xhdpi' }, { name: 'drawable-xxhdpi-icon.png', width: 144, height: 144, density: 'xxhdpi' }, { name: 'drawable-xxxhdpi-icon.png', width: 192, height: 192, density: 'xxxhdpi' }, ], nodeName: 'icon', nodeAttributes: ['src', 'density'], }, splash: { images: [ { name: 'drawable-land-ldpi-screen.png', width: 320, height: 240, density: 'land-ldpi', orientation: 'landscape' }, { name: 'drawable-land-mdpi-screen.png', width: 480, height: 320, density: 'land-mdpi', orientation: 'landscape' }, { name: 'drawable-land-hdpi-screen.png', width: 800, height: 480, density: 'land-hdpi', orientation: 'landscape' }, { name: 'drawable-land-xhdpi-screen.png', width: 1280, height: 720, density: 'land-xhdpi', orientation: 'landscape' }, { name: 'drawable-land-xxhdpi-screen.png', width: 1600, height: 960, density: 'land-xxhdpi', orientation: 'landscape' }, { name: 'drawable-land-xxxhdpi-screen.png', width: 1920, height: 1280, density: 'land-xxxhdpi', orientation: 'landscape' }, { name: 'drawable-port-ldpi-screen.png', width: 240, height: 320, density: 'port-ldpi', orientation: 'portrait' }, { name: 'drawable-port-mdpi-screen.png', width: 320, height: 480, density: 'port-mdpi', orientation: 'portrait' }, { name: 'drawable-port-hdpi-screen.png', width: 480, height: 800, density: 'port-hdpi', orientation: 'portrait' }, { name: 'drawable-port-xhdpi-screen.png', width: 720, height: 1280, density: 'port-xhdpi', orientation: 'portrait' }, { name: 'drawable-port-xxhdpi-screen.png', width: 960, height: 1600, density: 'port-xxhdpi', orientation: 'portrait' }, { name: 'drawable-port-xxxhdpi-screen.png', width: 1280, height: 1920, density: 'port-xxxhdpi', orientation: 'portrait' }, ], nodeName: 'splash', nodeAttributes: ['src', 'density'], }, }, ios: { icon: { images: [ { name: 'icon.png', width: 57, height: 57 }, { name: 'icon@2x.png', width: 114, height: 114 }, { name: 'icon-40.png', width: 40, height: 40 }, { name: 'icon-40@2x.png', width: 80, height: 80 }, { name: 'icon-40@3x.png', width: 120, height: 120 }, { name: 'icon-50.png', width: 50, height: 50 }, { name: 'icon-50@2x.png', width: 100, height: 100 }, { name: 'icon-60.png', width: 60, height: 60 }, { name: 'icon-60@2x.png', width: 120, height: 120 }, { name: 'icon-60@3x.png', width: 180, height: 180 }, { name: 'icon-72.png', width: 72, height: 72 }, { name: 'icon-72@2x.png', width: 144, height: 144 }, { name: 'icon-76.png', width: 76, height: 76 }, { name: 'icon-76@2x.png', width: 152, height: 152 }, { name: 'icon-83.5@2x.png', width: 167, height: 167 }, { name: 'icon-small.png', width: 29, height: 29 }, { name: 'icon-small@2x.png', width: 58, height: 58 }, { name: 'icon-small@3x.png', width: 87, height: 87 }, { name: 'icon-1024.png', width: 1024, height: 1024 }, ], nodeName: 'icon', nodeAttributes: ['src', 'width', 'height'], }, splash: { images: [ { name: 'Default-568h@2x~iphone.png', width: 640, height: 1136, orientation: 'portrait' }, { name: 'Default-667h.png', width: 750, height: 1334, orientation: 'portrait' }, { name: 'Default-736h.png', width: 1242, height: 2208, orientation: 'portrait' }, { name: 'Default-Landscape-736h.png', width: 2208, height: 1242, orientation: 'landscape' }, { name: 'Default-Landscape@2x~ipad.png', width: 2048, height: 1536, orientation: 'landscape' }, { name: 'Default-Landscape@~ipadpro.png', width: 2732, height: 2048, orientation: 'landscape' }, { name: 'Default-Landscape~ipad.png', width: 1024, height: 768, orientation: 'landscape' }, { name: 'Default-Portrait@2x~ipad.png', width: 1536, height: 2048, orientation: 'portrait' }, { name: 'Default-Portrait@~ipadpro.png', width: 2048, height: 2732, orientation: 'portrait' }, { name: 'Default-Portrait~ipad.png', width: 768, height: 1024, orientation: 'portrait' }, { name: 'Default@2x~iphone.png', width: 640, height: 960, orientation: 'portrait' }, { name: 'Default~iphone.png', width: 320, height: 480, orientation: 'portrait' }, { name: 'Default@2x~universal~anyany.png', width: 2732, height: 2732 }, ], nodeName: 'splash', nodeAttributes: ['src', 'width', 'height'], }, }, wp8: { icon: { images: [ { name: 'ApplicationIcon.png', width: 99, height: 99 }, { name: 'Background.png', width: 159, height: 159 }, ], nodeName: 'icon', nodeAttributes: ['src', 'width', 'height'], }, splash: { images: [ { name: 'SplashScreenImage.png', width: 768, height: 1280 }, ], nodeName: 'splash', nodeAttributes: ['src', 'width', 'height'], }, }, };