diff --git a/.build/check-image-package.mjs b/.build/check-image-package.mjs new file mode 100644 index 000000000..925f85ed0 --- /dev/null +++ b/.build/check-image-package.mjs @@ -0,0 +1,63 @@ +// Checks that an image package (png / pdf / eps) contains exactly one +// non-empty file per icon. Runs before publishing, so a conversion that was +// skipped (e.g. without `rsvg-convert`) or a stale `icons/` directory from a +// previous build cannot be released. +// +// Usage, from the package directory: node ../../.build/check-image-package.mjs +import { readdirSync, statSync } from 'fs'; +import path from 'path'; +import { getAllIcons } from './helpers.mjs'; + +const extension = process.argv[2]; + +if (!extension) { + console.error('Usage: check-image-package.mjs '); + process.exit(1); +} + +const listFiles = (dir) => { + try { + return readdirSync(dir).filter((file) => file.endsWith(`.${extension}`)); + } catch { + return []; + } +}; + +const sample = (files) => files.slice(0, 10).join(', ') + (files.length > 10 ? ', …' : ''); + +let failed = false; + +for (const [type, icons] of Object.entries(getAllIcons())) { + const dir = path.resolve('icons', type); + const expected = new Set(icons.map((icon) => `${icon.name}.${extension}`)); + const actual = listFiles(dir); + const actualSet = new Set(actual); + + const missing = [...expected].filter((file) => !actualSet.has(file)); + const extra = actual.filter((file) => !expected.has(file)); + const empty = actual.filter( + (file) => expected.has(file) && statSync(path.join(dir, file)).size === 0, + ); + + for (const [label, files] of [ + ['missing', missing], + ['not an icon anymore', extra], + ['empty', empty], + ]) { + if (files.length > 0) { + failed = true; + console.error(`${type}: ${files.length} ${label}: ${sample(files)}`); + } + } + + if (missing.length === 0 && extra.length === 0 && empty.length === 0) { + console.log(`${type}: ${actual.length} ${extension} files, all icons present`); + } +} + +if (failed) { + console.error( + `\nicons/ does not match the current icons. Rebuild the package before publishing.`, + ); + process.exit(1); +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b97cbdcc..67c83749a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,6 +62,11 @@ jobs: ICONS_LIMIT: 100 run: pnpm run build + - name: Check image packages + env: + ICONS_LIMIT: 100 + run: pnpm --filter @tabler/icons-png --filter @tabler/icons-pdf --filter @tabler/icons-eps run check + # The demo apps in test/test-* have no test script and import icons outside # the ICONS_LIMIT subset, so only the packages and the two guard suites are # selected. @tabler/icons-angular runs in headless Chrome via Karma, which diff --git a/packages/icons-eps/package.json b/packages/icons-eps/package.json index 2a007aba2..4f5993861 100644 --- a/packages/icons-eps/package.json +++ b/packages/icons-eps/package.json @@ -16,6 +16,8 @@ "scripts": { "build": "pnpm run clean && pnpm run copy:license && pnpm run build:icons", "build:icons": "node build.mjs", + "check": "node ../../.build/check-image-package.mjs eps", + "prepublishOnly": "pnpm run check", "clean": "find dist -mindepth 1 -delete 2>/dev/null; rm -rf dist && find . ! -name '.gitkeep' -path '*/icons/*' -exec rm -rf {} +", "copy:license": "cp ../../LICENSE ./LICENSE" }, diff --git a/packages/icons-pdf/package.json b/packages/icons-pdf/package.json index 94d305ccc..1c401b26a 100644 --- a/packages/icons-pdf/package.json +++ b/packages/icons-pdf/package.json @@ -21,6 +21,8 @@ "scripts": { "build": "pnpm run clean && pnpm run copy:license && pnpm run build:icons", "build:icons": "node build.mjs", + "check": "node ../../.build/check-image-package.mjs pdf", + "prepublishOnly": "pnpm run check", "clean": "find dist -mindepth 1 -delete 2>/dev/null; rm -rf dist && find . ! -name '.gitkeep' -path '*/icons/*' -exec rm -rf {} +", "copy:license": "cp ../../LICENSE ./LICENSE" }, diff --git a/packages/icons-png/package.json b/packages/icons-png/package.json index 5bedb02a0..fb25be8a2 100644 --- a/packages/icons-png/package.json +++ b/packages/icons-png/package.json @@ -21,6 +21,8 @@ "scripts": { "build": "pnpm run clean && pnpm run copy:license && pnpm run build:icons", "build:icons": "node build.mjs", + "check": "node ../../.build/check-image-package.mjs png", + "prepublishOnly": "pnpm run check", "clean": "find dist -mindepth 1 -delete 2>/dev/null; rm -rf dist && find . ! -name '.gitkeep' -path '*/icons/*' -exec rm -rf {} +", "copy:license": "cp ../../LICENSE ./LICENSE" },