/** * Генерация SVG-спрайтов и TypeScript-типов имён иконок. * * Читает подпапки из ASSETS_DIR, для каждой собирает SVG в спрайт (stack или symbol) * и генерирует .generated.ts файл с union-типом имён иконок. * * Режим спрайта определяется суффиксом папки: «icons?symbol» → symbol, иначе stack. * * Запуск: npm run sprite */ const fs = require('fs') const path = require('path') const SVGSpriter = require('svg-sprite') const color = require('colorette') const ROOT = process.cwd() /** Папка с исходными SVG-файлами. */ const ASSETS_DIR = path.join(ROOT, 'src/shared/sprites') /** Папка для сгенерированных спрайтов. */ const DEST_DIR = path.join(ROOT, 'public/img/sprites') /** * Преобразует kebab-case строку в PascalCase. */ const toPascalCase = (str) => str.replace(/(^|[-_])([a-z])/g, (_, __, c) => c.toUpperCase()) /** * Возвращает конфигурацию режима для svg-sprite. */ const getModeConfig = (mode, destDir) => ({ dest: destDir, sprite: `sprite.${mode}.svg`, example: true, rootviewbox: false, }) /** * Генерирует TypeScript-файл с union-типом имён иконок спрайта. */ const generateIconNames = (folderName, svgFiles) => { const names = svgFiles .map((filePath) => path.basename(filePath, '.svg')) .sort() const typeName = `${toPascalCase(folderName)}IconName` const content = [ '/**', ` * Имена иконок спрайта «${folderName}».`, ' * @generated — файл создан автоматически (npm run sprite), не редактировать вручную.', ' */', `export type ${typeName} =`, names.map((name) => ` | '${name}'`).join('\n'), '', ].join('\n') const outputPath = path.join(ASSETS_DIR, `${folderName}.generated.ts`) fs.writeFileSync(outputPath, content) console.log( color.green(`Generated types: ${folderName}.generated.ts (${names.length} icons)`), ) } /** * Обрабатывает одну папку со спрайтами. */ const processFolder = (fullFolderName) => { const folderPath = path.join(ASSETS_DIR, fullFolderName) if (!fs.lstatSync(folderPath).isDirectory()) { return } const hasCustomMode = fullFolderName.includes('?') const parts = fullFolderName.split('?') const mode = hasCustomMode ? parts.pop() : 'stack' const folderName = parts[0] const svgFiles = fs .readdirSync(folderPath) .filter((file) => file.endsWith('.svg')) .map((file) => path.join(folderPath, file)) if (!svgFiles.length) { return } const config = { log: 'debug', mode: { [mode]: getModeConfig(mode, path.join(DEST_DIR, folderName)), }, } const spriter = new SVGSpriter(config) for (const fileName of svgFiles) { spriter.add(fileName, null, fs.readFileSync(fileName, 'utf-8')) } spriter.compile((error, result) => { if (error) { console.log(color.red(error.message)) return } for (const modeResult of Object.values(result)) { for (const resource of Object.values(modeResult)) { fs.mkdirSync(path.dirname(resource.path), { recursive: true }) fs.writeFileSync(resource.path, resource.contents) } } }) generateIconNames(folderName, svgFiles) } try { const entries = fs.readdirSync(ASSETS_DIR) entries.forEach(processFolder) } catch (err) { console.log(color.red(err.message)) }