UNPKG

iso-3166-1-flags-sprite-generator

Version:
110 lines (109 loc) 6.42 kB
import * as glob from 'glob'; import * as path from 'path'; import * as fs from 'fs'; import * as chalk from 'chalk'; import { isNullOrUndefined } from '@qntm-code/utils'; import * as sharp from 'sharp'; import * as Spritesmith from 'spritesmith'; import { cssPropertiesToString } from './css-properties-to-string'; const TEMP_DIRECTORY = path.join(__dirname, '..', `tmp`).replace(path.join(__dirname, '..', '..'), ''); export default async function createFlagSprite(spriteDestination, cssDestination, options) { const mergedOptions = { center: true, classPrefix: 'flag', cssFileName: 'flags', demo: true, demoDestination: 'flags-demo', dimensionsSuffix: 'dims', width: 60, spriteFileName: 'flag-sprite', ...options, }; if (!mergedOptions.silent) { console.log(chalk.blue('Generating flag sprite')); } fs.mkdirSync(TEMP_DIRECTORY.substring(1), { recursive: true }); const originalPNGPaths = glob.sync(`${path.join(__dirname, '..')}/png/*.png`); const pngPaths = []; const originalWidths = (await Promise.all(originalPNGPaths.map(async (originalPath) => (await sharp(originalPath).metadata()).width))).filter(item => !isNullOrUndefined(item)); const widestOriginal = Math.max(...originalWidths); const maxIconWidth = Math.min(...originalWidths); mergedOptions.width = Math.max(Math.min(mergedOptions.width, maxIconWidth), 1); const flagHeight = Math.round((mergedOptions.width / widestOriginal) * 240); const results = await Promise.all(originalPNGPaths.map(async (originalPath) => { const pngPath = `${TEMP_DIRECTORY.substring(1)}/${path.basename(originalPath, '.png')}.png`; pngPaths.push(pngPath); const png = sharp(originalPath).resize(undefined, flagHeight); await png.toFile(pngPath); return sharp(pngPath).metadata(); })); const widths = results.map(({ width }) => width || 0); const maxWidth = Math.max(...widths); const padding = Math.ceil((maxWidth - Math.min(...widths)) / 2); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call Spritesmith.run({ src: pngPaths, padding }, (_, { coordinates, properties, image, }) => { const spriteFilename = `${mergedOptions.spriteFileName}.png`; const spriteFilePath = `${spriteDestination}/${spriteFilename}`; const spriteUrl = mergedOptions.spriteUrl ? `${mergedOptions.spriteUrl}/${spriteFilename}` : spriteFilePath; const demoSpriteName = 'sprite.png'; const cssFilename = `${mergedOptions.cssFileName}.css`; const cssFilePath = `${cssDestination}/${cssFilename}`; const demoCssName = `flags.css`; let css = createCssStart(properties, spriteUrl, maxWidth, flagHeight, mergedOptions); let demoCSS = createCssStart(properties, demoSpriteName, maxWidth, flagHeight, mergedOptions); let html = `<!DOCTYPE html><html><head><title>Flags Sprite</title><link rel="stylesheet" type="text/css" href="${demoCssName}"><style>body { font-family: sans-serif; text-align: center; margin 5px; } body > div { display: inline-block; margin: 5px; } .${mergedOptions.classPrefix}{ width: ${maxWidth}px; height: ${flagHeight}px; }</style></head><body><h1>Created by iso-3166-1-flags-sprite-generator</h1>`; Object.entries(coordinates).forEach(([pngPath, { x, y, width, height }]) => { const code = path.basename(pngPath, '.png'); const className = `${mergedOptions.classPrefix}-${mergedOptions.lowecaseAlpha2 ? code.toLowerCase() : code}`; const cssProperties = { ['background-position']: `${((x - (mergedOptions.center ? (maxWidth - width) / 2 : 0)) / (properties.width - maxWidth)) * 100}% ${(y / (properties.height - flagHeight)) * 100}%`, }; const flagClass = `\n\n.${className} {${cssPropertiesToString(cssProperties)}\n}`; css += flagClass; demoCSS += flagClass; if (mergedOptions.dimensionsClasses) { const flagDimensionsClass = `\n\n.${className}-${mergedOptions.dimensionsSuffix} {${cssPropertiesToString({ width: `${width}px`, height: `${height}px`, })}\n}`; css += flagDimensionsClass; demoCSS += flagDimensionsClass; } html += `<div><div class="${mergedOptions.classPrefix} ${className}"></div><p>${className}</p></div>`; }); html += `</body></html>`; fs.mkdirSync(spriteDestination, { recursive: true }); fs.mkdirSync(cssDestination, { recursive: true }); fs.createWriteStream(spriteFilePath).write(image); fs.writeFileSync(cssFilePath, css, 'utf-8'); const demoFilePath = `${mergedOptions.demoDestination}/index.html`; if (mergedOptions.demo) { fs.mkdirSync(mergedOptions.demoDestination, { recursive: true }); fs.writeFileSync(demoFilePath, html, 'utf-8'); fs.createWriteStream(`${mergedOptions.demoDestination}/${demoSpriteName}`).write(image); fs.writeFileSync(`${mergedOptions.demoDestination}/${demoCssName}`, demoCSS, 'utf-8'); } if (!mergedOptions.silent) { console.log(chalk.greenBright('Successfully generated flags sprite')); console.log(chalk.green(`Sprite: ${spriteFilePath}`)); console.log(chalk.green(`CSS: ${cssFilePath}`)); if (mergedOptions.demo) { console.log(chalk.green(`Demo: ${demoFilePath}`)); } } }); } function createCssStart(properties, spriteUrl, maxWidth, flagHeight, mergedOptions) { const baseCss = { display: `inline-block`, background: ` url('${spriteUrl}') no-repeat`, ['background-size']: `${(properties.width / maxWidth) * 100}% ${(properties.height / flagHeight) * 100}%`, ['vertical-align']: `middle`, overflow: `hidden`, }; const dimensionsCss = { width: `${mergedOptions.width}px`, height: `${flagHeight}px`, }; return `/**\n * GENERATED BY iso-3166-1-flags-sprite-generator\n */\n\n.${mergedOptions.classPrefix} {${cssPropertiesToString(baseCss)}\n}\n\n.${mergedOptions.classPrefix}-${mergedOptions.dimensionsSuffix} {${cssPropertiesToString(dimensionsCss)}\n}`; }