feat(branding): install @vite-pwa/assets-generator + config
Adds pwa-assets.config.ts that reads $BRAND_DIR (default
./branding/default) and $BRAND_APP (optional per-standalone
override), resolves logo.svg/logo.png with documented fallback
order, and emits the existing icon set (favicon.ico,
icon-{192,512}.png, icon-maskable-{192,512}.png,
apple-touch-icon.png) into public/icons/.
Generator outputs alongside its source, so the config stages the
brand source into public/icons/.brand-source.{svg,png}; gitignoring
public/icons/ covers both staged source and generated icons in one
line.
Adds pnpm script `generate-pwa-assets`. Vite configs / HTML <link>
href updates come in follow-up commits; this commit alone produces
the icon set under public/icons/ but doesn't yet replace the
committed public/*.png binaries.
Part of aiolabs/webapp#95 (brand kit architecture).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a8c997ca8d
commit
50a345ce4e
4 changed files with 164 additions and 2 deletions
54
pwa-assets.config.ts
Normal file
54
pwa-assets.config.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { defineConfig } from '@vite-pwa/assets-generator/config'
|
||||
import { copyFileSync, existsSync, mkdirSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
|
||||
const BRAND_DIR = process.env.BRAND_DIR ?? './branding/default'
|
||||
const BRAND_APP = process.env.BRAND_APP ?? ''
|
||||
|
||||
const candidates: string[] = []
|
||||
if (BRAND_APP) {
|
||||
candidates.push(
|
||||
join(BRAND_DIR, 'icons', BRAND_APP, 'logo.svg'),
|
||||
join(BRAND_DIR, 'icons', BRAND_APP, 'logo.png'),
|
||||
)
|
||||
}
|
||||
candidates.push(
|
||||
join(BRAND_DIR, 'logo.svg'),
|
||||
join(BRAND_DIR, 'logo.png'),
|
||||
)
|
||||
|
||||
const source = candidates.find((p) => existsSync(p))
|
||||
if (!source) {
|
||||
throw new Error(
|
||||
`No brand logo found. Tried:\n ${candidates.join('\n ')}\n` +
|
||||
`See branding/README.md for the brand kit contract.`,
|
||||
)
|
||||
}
|
||||
|
||||
// The CLI emits next to the source. Stage into public/icons/ so generated
|
||||
// PNGs are served at /icons/<name>.png and a single .gitignore line covers
|
||||
// the whole tree.
|
||||
const stagingDir = resolve('public/icons')
|
||||
mkdirSync(stagingDir, { recursive: true })
|
||||
const sourceExt = source.toLowerCase().endsWith('.svg') ? '.svg' : '.png'
|
||||
const stagedSource = join(stagingDir, `.brand-source${sourceExt}`)
|
||||
copyFileSync(source, stagedSource)
|
||||
|
||||
export default defineConfig({
|
||||
headLinkOptions: { preset: '2023' },
|
||||
preset: {
|
||||
transparent: {
|
||||
sizes: [192, 512],
|
||||
favicons: [[48, 'favicon.ico']],
|
||||
},
|
||||
maskable: { sizes: [192, 512] },
|
||||
apple: { sizes: [180] },
|
||||
assetName: (type, size) => {
|
||||
if (type === 'transparent') return `icon-${size.width}.png`
|
||||
if (type === 'maskable') return `icon-maskable-${size.width}.png`
|
||||
if (type === 'apple') return 'apple-touch-icon.png'
|
||||
throw new Error(`Unknown asset type: ${type}`)
|
||||
},
|
||||
},
|
||||
images: [stagedSource],
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue