70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
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/<kebab-name>.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}`)
|