import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync } from 'fs' import { resolve, join } from 'path' import { createRequire } from 'module' /** * Generate static SVG assets + an SCSS partial that re-applies HOT v17 classic * theme icons via real URLs (not data: URIs). * * Why: deployed app runs under CSP `img-src 'self'`. HOT v17's classic theme * embeds icons as `data:image/svg+xml,...` in `-webkit-mask-image` rules, which * the CSP blocks. We switch to `ht-theme-classic-no-icons.min.css` and re-add * the icon rules pointing at same-origin SVG files emitted from this script. * * Inputs (HOT's own modules, so semantic names + selector list track upstream): * handsontable/themes/theme/classic → { classicTheme: { icons } } * handsontable/themes/static/variables/helpers/iconsMap → iconsMap(icons, themePrefix) * * Outputs: * client/src/assets/hot-icons/.svg * client/src/_hot-icons.scss * * Idempotent: clears the output dir and rewrites both outputs each run. * Skips silently if handsontable isn't installed yet (pre-install runs). */ const require = createRequire(import.meta.url) const ASSETS_DIR = resolve('src/assets/hot-icons') const SCSS_OUT = resolve('src/_hot-icons.scss') const ASSET_URL_PREFIX = './assets/hot-icons/' const themePath = resolve('node_modules/handsontable/themes/theme/classic.js') const mapPath = resolve('node_modules/handsontable/themes/static/variables/helpers/iconsMap.js') if (!existsSync(themePath) || !existsSync(mapPath)) { console.log('skip: handsontable theme modules not found (likely pre-install run)') process.exit(0) } const { classicTheme } = require(themePath) const { iconsMap } = require(mapPath) const icons = classicTheme.icons const cssTemplate = iconsMap(icons, 'ht-theme-classic') const kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase() rmSync(ASSETS_DIR, { recursive: true, force: true }) mkdirSync(ASSETS_DIR, { recursive: true }) const writeMap = {} for (const [name, dataUri] of Object.entries(icons)) { if (typeof dataUri !== 'string' || !dataUri.startsWith('data:image/svg+xml')) continue const decoded = decodeURIComponent(dataUri.replace(/^data:image\/svg\+xml(;charset=utf-8)?,/, '')) const fname = kebab(name) + '.svg' writeFileSync(join(ASSETS_DIR, fname), decoded) writeMap[dataUri] = ASSET_URL_PREFIX + fname } let scss = cssTemplate for (const [uri, url] of Object.entries(writeMap)) { scss = scss.split(`url("${uri}")`).join(`url("${url}")`) } const header = '/* Auto-generated by scripts/gen-hot-icons.mjs — do not edit by hand.\n' + ' Regenerated on postinstall; rerun manually via `node scripts/gen-hot-icons.mjs`. */\n\n' writeFileSync(SCSS_OUT, header + scss + '\n') console.log(`hot-icons: wrote ${Object.keys(writeMap).length} SVGs + ${SCSS_OUT}`)