api.js/src/http/responses/css.ts

153 lines
4.1 KiB
TypeScript

import type { FastifyReply, FastifyRequest } from 'fastify';
import { stringToColor } from '@iconify/utils/lib/colors';
import { getIconsCSS } from '@iconify/utils/lib/css/icons';
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons';
import { iconSets } from '../../data/icon-sets';
import type { IconCSSIconSetOptions } from '@iconify/utils/lib/css/types';
import { paramToBoolean } from '../../misc/bool';
/**
* Check selector for weird stuff
*/
function checkSelector(value: string | undefined): boolean {
if (typeof value !== 'string') {
return false;
}
const cleanValue = value.replaceAll('{name}', '').replaceAll('{prefix}', '');
return cleanValue.indexOf('{') === -1 && cleanValue.indexOf('}') === -1;
}
/**
* Generate icons style
*/
export function generateIconsStyleResponse(prefix: string, query: FastifyRequest['query'], res: FastifyReply) {
const q = (query || {}) as Record<string, string>;
const names = q.icons?.split(',');
if (!names || !names.length) {
// Missing or invalid icons parameter
res.send(404);
return;
}
// Get icon set
const iconSet = iconSets[prefix];
if (!iconSet) {
// No such icon set
res.send(404);
return;
}
// Get icons
getStoredIconsData(iconSet.item, names, (data) => {
// Options
const options: IconCSSIconSetOptions = {};
const qOptions = q as IconCSSIconSetOptions;
if (typeof qOptions.format === 'string') {
const format = qOptions.format;
switch (format) {
case 'compact':
case 'compressed':
case 'expanded':
options.format = format;
}
}
// 'color': string
// Sets color for monotone images
const color = qOptions.color;
if (typeof color === 'string' && stringToColor(color)) {
options.color = color;
}
// 'mode': string
// Forces mode
// Alias for 'background': 'bg'
const mode = qOptions.mode;
if (mode) {
switch (mode) {
case 'background':
case 'mask':
options.mode = mode;
default:
if ((mode as string) === 'bg') {
options.mode = 'background';
}
}
}
// 'forceSquare': boolean
// Forces icon to be square, regardless of width/height ratio
// Aliases: 'square', 'force-square'
const forceSquare = paramToBoolean(q.square || q.forceSquare || q['force-square'], void 0);
if (typeof forceSquare === 'boolean') {
options.forceSquare = forceSquare;
}
// 'pseudoSelector': boolean
// Adds `content: '';` to common selector. Useful when selector is a pseudo-selector
// Aliases: 'pseudo', 'pseudo-selector'
const pseudoSelector = paramToBoolean(q.pseudo || q.pseudoSelector || q['pseudo-selector'], void 0);
if (typeof pseudoSelector === 'boolean') {
options.pseudoSelector = pseudoSelector;
}
// 'commonSelector': string
// Common selector for all requested icons
// Alias: 'common'
const commonSelector = qOptions.commonSelector || q.common;
if (checkSelector(commonSelector)) {
options.commonSelector = commonSelector;
}
// 'iconSelector': string
// Icon selector
// Alias: 'selector'
const iconSelector = qOptions.iconSelector || q.selector;
if (checkSelector(iconSelector)) {
options.iconSelector = iconSelector;
}
// 'overrideSelector': string
// Selector for rules in icon that override common rules
// Alias: 'override'
const overrideSelector = qOptions.overrideSelector || q.override;
if (checkSelector(overrideSelector)) {
options.overrideSelector = overrideSelector;
}
// 'varName': string
// Variable name
// Alias: 'var'
const varName = q.varName || q.var;
if (typeof varName === 'string' && varName.match(/^[a-z0-9_-]*$/)) {
if (!varName || varName === 'null' || !paramToBoolean(varName, true)) {
options.varName = null;
} else {
options.varName = varName;
}
}
// Generate css
const css = getIconsCSS(
{
// Data
...data,
// Info to detect palette
info: iconSet.item.info,
},
names,
options
);
// Send CSS, optionally as attachment
if (q.download) {
res.header('Content-Disposition', 'attachment; filename="' + prefix + '.css"');
}
res.type('text/css; charset=utf-8').send(css);
});
}