feat(branding): brand kit architecture (Phase 1) #96
10 changed files with 45 additions and 9 deletions
feat(branding): auto-generate icons on vite build/dev start
vite-branding.ts now exports brandAssetsPlugin() — a Vite plugin whose buildStart hook runs scripts/generate-pwa-assets.mjs once per build / dev-server start. All 9 vite configs register it first in plugins[], so PWA icons under public/icons/ are guaranteed to exist before VitePWA's includeAssets / manifest.icons get processed and before the public/ → dist/ copy. Removes the "did you remember to pnpm generate-pwa-assets?" failure mode. Dev mode now also auto-populates icons (no more dev 404s on /icons/favicon.ico). Verified build from clean state (no public/icons/ pre-existing): the plugin generates, all 6 icons land in dist-wallet/icons/. Part of aiolabs/webapp#95. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit
3efae30e84
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { spawnSync } from 'node:child_process'
|
||||||
import { readFileSync } from 'node:fs'
|
import { readFileSync } from 'node:fs'
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Absolute path to the active brand kit. Deployers point this at their
|
* Absolute path to the active brand kit. Deployers point this at their
|
||||||
|
|
@ -51,3 +53,28 @@ export const brandAlias = {
|
||||||
export function brandManifestName(appLabel?: string): string {
|
export function brandManifestName(appLabel?: string): string {
|
||||||
return appLabel ? `${brand.name} ${appLabel}` : brand.name
|
return appLabel ? `${brand.name} ${appLabel}` : brand.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin: regenerate PWA icons under public/icons/ once per build
|
||||||
|
* / dev-server start, so vite.<app>.config.ts's includeAssets +
|
||||||
|
* manifest.icons always have something to include. Source resolution
|
||||||
|
* lives in pwa-assets.config.ts.
|
||||||
|
*/
|
||||||
|
export function brandAssetsPlugin(): Plugin {
|
||||||
|
let generated = false
|
||||||
|
return {
|
||||||
|
name: 'brand-assets-generator',
|
||||||
|
buildStart() {
|
||||||
|
if (generated) return
|
||||||
|
const { status } = spawnSync(
|
||||||
|
'node',
|
||||||
|
[resolve('scripts/generate-pwa-assets.mjs')],
|
||||||
|
{ stdio: 'inherit' },
|
||||||
|
)
|
||||||
|
if (status !== 0) {
|
||||||
|
throw new Error('pwa-assets-generator failed; see output above')
|
||||||
|
}
|
||||||
|
generated = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function chatHtmlPlugin(): Plugin {
|
function chatHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
chatHtmlPlugin(),
|
chatHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig } from 'vite'
|
||||||
import Inspect from 'vite-plugin-inspect'
|
import Inspect from 'vite-plugin-inspect'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
//
|
//
|
||||||
|
|
@ -26,6 +26,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
Inspect(),
|
Inspect(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias, brandManifestName } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin, brandManifestName } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to events.html
|
* Plugin to rewrite dev server requests to events.html
|
||||||
|
|
@ -58,6 +58,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
eventsHtmlPlugin(),
|
eventsHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function forumHtmlPlugin(): Plugin {
|
function forumHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
forumHtmlPlugin(),
|
forumHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to libra.html
|
* Plugin to rewrite dev server requests to libra.html
|
||||||
|
|
@ -51,6 +51,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
libraHtmlPlugin(),
|
libraHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function marketHtmlPlugin(): Plugin {
|
function marketHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
marketHtmlPlugin(),
|
marketHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function restaurantHtmlPlugin(): Plugin {
|
function restaurantHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -51,6 +51,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
restaurantHtmlPlugin(),
|
restaurantHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function tasksHtmlPlugin(): Plugin {
|
function tasksHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
tasksHtmlPlugin(),
|
tasksHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import { brand, brandAlias } from './vite-branding'
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to wallet.html
|
* Plugin to rewrite dev server requests to wallet.html
|
||||||
|
|
@ -50,6 +50,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
walletHtmlPlugin(),
|
walletHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue