@payfit/unity-illustrations 2.21.16 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payfit/unity-illustrations",
3
- "version": "2.21.16",
3
+ "version": "2.22.0",
4
4
  "module": "./dist/esm/index.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -39,7 +39,7 @@
39
39
  "@payfit/hr-app-eslint": "0.0.0-use.local",
40
40
  "@payfit/hr-apps-tsconfigs": "0.0.0-use.local",
41
41
  "@payfit/storybook-config": "0.0.0-use.local",
42
- "@payfit/unity-themes": "2.21.16",
42
+ "@payfit/unity-themes": "2.22.0",
43
43
  "@payfit/vite-configs": "0.0.0-use.local",
44
44
  "@storybook/addon-a11y": "10.3.3",
45
45
  "@storybook/addon-docs": "10.3.3",
@@ -58,7 +58,7 @@
58
58
  "vite": "7.1.12"
59
59
  },
60
60
  "peerDependencies": {
61
- "@payfit/unity-themes": "2.21.16",
61
+ "@payfit/unity-themes": "2.22.0",
62
62
  "react": "18.3.1",
63
63
  "react-dom": "18.3.1"
64
64
  }
@@ -1,11 +1,19 @@
1
+ import { existsSync, readFileSync } from 'fs'
1
2
  import fs from 'fs/promises'
2
3
  import path from 'path'
3
4
 
4
5
  import type { Config } from 'svgo'
5
6
 
7
+ import type { ValidationEntry } from '../../scripts/shared/validate-size'
8
+ import type { SizeGuardrailsConfig } from '../../scripts/steps/download-assets'
9
+
6
10
  import imageSize from 'image-size'
7
11
  import { loadConfig, optimize } from 'svgo'
8
12
 
13
+ import {
14
+ parseSvgWidth,
15
+ reportViolations,
16
+ } from '../../scripts/shared/validate-size'
9
17
  import { generateAssetModule } from './templates/assetTemplate'
10
18
  import { generateBaseTypes } from './templates/baseTypes'
11
19
  import { generateIndexFile } from './templates/indexTemplate'
@@ -15,6 +23,7 @@ const illustrationsSvgDir = path.resolve('./assets/')
15
23
  const srcRoot = path.resolve('./src')
16
24
  const outputDir = path.join(srcRoot, 'generated')
17
25
  const assetsOutputDir = path.join(outputDir, 'assets') // Optimized assets go here
26
+ const configPath = path.resolve('./.figma-sync.json')
18
27
 
19
28
  const verboseFromArg = process.argv.some(
20
29
  arg => arg === '--verbose' || arg === '-v',
@@ -226,6 +235,24 @@ async function processAsset(assetPath: string) {
226
235
  }
227
236
  }
228
237
 
238
+ function loadSizeGuardrails(): SizeGuardrailsConfig | undefined {
239
+ if (!existsSync(configPath)) {
240
+ console.log('ℹ️ No .figma-sync.json found, skipping size guardrails')
241
+ return undefined
242
+ }
243
+
244
+ const config = JSON.parse(readFileSync(configPath, 'utf-8')) as {
245
+ sizeGuardrails?: SizeGuardrailsConfig
246
+ }
247
+
248
+ if (!config.sizeGuardrails) {
249
+ console.log('ℹ️ No sizeGuardrails config, skipping size validation')
250
+ return undefined
251
+ }
252
+
253
+ return config.sizeGuardrails
254
+ }
255
+
229
256
  async function main() {
230
257
  console.log('🎨 Generating and optimizing illustration assets...')
231
258
  if (isVerbose) {
@@ -241,6 +268,11 @@ async function main() {
241
268
  await fs.writeFile(path.join(outputDir, 'types.ts'), baseTypesContent, 'utf8')
242
269
  verboseLog('✅ Generated base types')
243
270
 
271
+ // Load size guardrails config
272
+ const sizeGuardrails = loadSizeGuardrails()
273
+ const violations: ValidationEntry[] = []
274
+ const warnings: ValidationEntry[] = []
275
+
244
276
  // Discover all asset files
245
277
  const assetFiles = await getAllAssetFiles(illustrationsSvgDir)
246
278
  console.log(`🔍 Found ${assetFiles.length} assets to process`)
@@ -256,6 +288,57 @@ async function main() {
256
288
  try {
257
289
  const component = await processAsset(assetPath)
258
290
 
291
+ // Validate optimized SVG against size guardrails
292
+ if (sizeGuardrails && component.type === 'svg') {
293
+ const optimizedContent = await fs.readFile(
294
+ component.optimizedPath,
295
+ 'utf8',
296
+ )
297
+ const optimizedSize = optimizedContent.length
298
+ const width = parseSvgWidth(optimizedContent)
299
+
300
+ const exceedsFileSize = optimizedSize > sizeGuardrails.maxFileSize
301
+ const exceedsWidth =
302
+ width !== undefined && width > sizeGuardrails.maxWidth
303
+ const isAllowlisted = sizeGuardrails.allowlist.includes(
304
+ component.originalFileName,
305
+ )
306
+
307
+ if (exceedsFileSize || exceedsWidth) {
308
+ const details: string[] = []
309
+ if (exceedsFileSize) {
310
+ details.push(
311
+ `${(optimizedSize / 1024).toFixed(1)}KB after SVGO (limit: ${(
312
+ sizeGuardrails.maxFileSize / 1024
313
+ ).toFixed(0)}KB)`,
314
+ )
315
+ }
316
+ if (exceedsWidth) {
317
+ details.push(
318
+ `${width}px width (limit: ${sizeGuardrails.maxWidth}px)`,
319
+ )
320
+ }
321
+
322
+ const detailStr = details.join('; ')
323
+
324
+ if (isAllowlisted) {
325
+ const msg = `${component.originalFileName}: ${detailStr}. Consider simplifying this illustration in Figma.`
326
+ warnings.push({
327
+ filename: component.originalFileName,
328
+ message: msg,
329
+ })
330
+ console.warn(` ⚠️ [Allowlisted] ${msg}`)
331
+ } else {
332
+ const msg = `${component.originalFileName}: ${detailStr}. Automatic optimization could not reduce below limit — simplify in Figma.`
333
+ violations.push({
334
+ filename: component.originalFileName,
335
+ message: msg,
336
+ })
337
+ console.error(` ❌ ${msg}`)
338
+ }
339
+ }
340
+ }
341
+
259
342
  // Generate individual asset module
260
343
  const moduleContent = generateAssetModule(
261
344
  component.name,
@@ -338,6 +421,11 @@ async function main() {
338
421
  console.log(
339
422
  ` - Total Animated: ${components.filter(c => c.animated).length}`,
340
423
  )
424
+
425
+ // Report size guardrail results
426
+ if (sizeGuardrails) {
427
+ reportViolations(violations, warnings, sizeGuardrails.strict)
428
+ }
341
429
  }
342
430
 
343
431
  main().catch((err: unknown) => {