11import fs from 'fs' ;
2- import { copyFile , open } from 'fs/promises' ;
2+ import { copyFile } from 'fs/promises' ;
33import path from 'path' ;
44import sharp from 'sharp' ;
5+ import { Plugin } from 'vite' ;
56import type { ManifestOptions } from 'vite-plugin-pwa' ;
67
7- function findBrandingFile ( filePath : string ) : string | null {
8+ type BrandingFile = {
9+ readonly pathname : string ;
10+ readonly filename : string ;
11+ readonly isDefault : boolean ;
12+ readonly isCustom : boolean ;
13+ }
14+
15+ function findBrandingFile ( filePathInput : string ) : BrandingFile | null {
816 const customFilePath = path . join (
9- path . dirname ( filePath ) , "custom" , path . basename ( filePath )
17+ path . dirname ( filePathInput ) , "custom" , path . basename ( filePathInput )
1018 ) ;
1119
12- if ( fs . existsSync ( customFilePath ) ) return customFilePath ;
13- if ( fs . existsSync ( filePath ) ) return filePath ;
14- return null
20+ const hasDefault = fs . existsSync ( filePathInput ) ;
21+ const hasCustom = fs . existsSync ( customFilePath ) ;
22+
23+ if ( ! hasDefault && ! hasCustom ) {
24+ return null ;
25+ }
26+
27+ const pathname = hasCustom ? customFilePath : filePathInput ;
28+ const filename = path . basename ( pathname ) ;
29+
30+ return {
31+ pathname,
32+ filename,
33+ isDefault : hasDefault && ! hasCustom ,
34+ isCustom : hasCustom ,
35+ }
1536}
1637
17- function findLogoFile ( baseDir : string , name : string ) : string | null {
18- const svgPath = findBrandingFile ( path . join ( baseDir , `${ name } .svg` ) ) ;
19- const pngPath = findBrandingFile ( path . join ( baseDir , `${ name } .png` ) ) ;
38+ function findLogoFile ( baseDir : string , name : string ) : BrandingFile | null {
39+ const svgFile = findBrandingFile ( path . join ( baseDir , `${ name } .svg` ) ) ;
40+ const pngFile = findBrandingFile ( path . join ( baseDir , `${ name } .png` ) ) ;
2041
21- if ( svgPath ) return svgPath ;
22- if ( pngPath ) return pngPath ;
42+ if ( svgFile ?. isCustom ) return svgFile ;
43+ if ( pngFile ?. isCustom ) return pngFile ;
44+ if ( svgFile ?. isDefault ) return svgFile ;
45+ if ( pngFile ?. isDefault ) return pngFile ;
2346 return null ;
2447}
2548
49+ type LogoFiles = Record < `logo_${'light' | 'dark' } `, BrandingFile > ;
50+
51+ function findLogoFiles ( sourceDir : string ) : LogoFiles {
52+ const files : Partial < LogoFiles > = { } ;
53+
54+ for ( const logoId of [ "logo_light" , "logo_dark" ] as const ) {
55+ const logoFile = findLogoFile ( sourceDir , logoId ) ;
56+ if ( ! logoFile ) {
57+ throw new Error ( `${ logoId } not found` ) ;
58+ }
59+
60+ files [ logoId ] = logoFile
61+ }
62+
63+ return files as LogoFiles ;
64+ }
65+
2666type GenerateAllIconsOptions = {
2767 sourceDir : string ;
2868 publicDir : string ;
@@ -38,28 +78,23 @@ async function generateAllIcons({
3878 appleTouchIcon,
3979 manifestIconSizes,
4080} : GenerateAllIconsOptions ) : Promise < ManifestOptions [ 'icons' ] > {
41- const faviconPath = findBrandingFile ( path . join ( sourceDir , "favicon.ico" ) ) ;
42- if ( ! faviconPath ) {
81+ const favicon = findBrandingFile ( path . join ( sourceDir , "favicon.ico" ) ) ;
82+ if ( ! favicon ) {
4383 throw new Error ( "favicon not found" ) ;
4484 }
4585
46- const logoLightPath = findLogoFile ( sourceDir , 'logo_light' ) ;
47- if ( ! logoLightPath ) {
48- throw new Error ( "light mode logo not found" ) ;
49- }
50-
51- const logoDarkPath = findLogoFile ( sourceDir , 'logo_dark' ) ;
52- if ( ! logoDarkPath ) {
53- throw new Error ( "dark mode logo not found" ) ;
54- }
86+ const {
87+ logo_light : logoLight ,
88+ logo_dark : logoDark ,
89+ } = findLogoFiles ( sourceDir ) ;
5590
56- // Full scale svgs
91+ // Full scale logos
5792 if ( copySource !== false ) {
5893 await Promise . all ( [
59- copyFile ( logoLightPath , path . join ( publicDir , path . basename ( logoLightPath ) ) ) ,
60- copyFile ( logoDarkPath , path . join ( publicDir , path . basename ( logoDarkPath ) ) ) ,
61- copyFile ( faviconPath , path . join ( publicDir , path . basename ( faviconPath ) ) ) . catch ( ( ) => {
62- console . warn ( `No file ${ faviconPath } was found, skipping...` ) ;
94+ copyFile ( logoLight . pathname , path . join ( publicDir , logoLight . filename ) ) ,
95+ copyFile ( logoDark . pathname , path . join ( publicDir , logoDark . filename ) ) ,
96+ copyFile ( favicon . pathname , path . join ( publicDir , path . basename ( favicon . filename ) ) ) . catch ( ( ) => {
97+ console . warn ( `No file ${ favicon } was found, skipping...` ) ;
6398 } ) ,
6499 ] ) ;
65100 }
@@ -72,7 +107,7 @@ async function generateAllIcons({
72107
73108 // Apple touch icon
74109 if ( appleTouchIcon !== false ) {
75- sharp ( logoDarkPath )
110+ sharp ( logoDark . pathname )
76111 . png ( )
77112 . resize ( 180 , 180 )
78113 . toFile ( path . join ( iconsDir , 'apple-touch-icon.png' ) ) ;
@@ -81,7 +116,7 @@ async function generateAllIcons({
81116 // Manifest icons
82117 const icons : ManifestOptions [ 'icons' ] = [ ] ;
83118
84- const manifestIcon = sharp ( logoDarkPath ) . png ( )
119+ const manifestIcon = sharp ( logoDark . pathname ) . png ( )
85120
86121 for ( const size of manifestIconSizes ) {
87122 const sizeStr = `${ size } x${ size } ` ;
@@ -152,10 +187,21 @@ export async function generateManifest(env: Record<string, string|null|undefined
152187 } ;
153188}
154189
155- export function ManifestPlugin ( env ) {
190+ export function ManifestPlugin ( env ) : Plugin {
156191 return {
157192 name : 'manifest-plugin' ,
158193
194+ config ( ) {
195+ const { logo_light, logo_dark } = findLogoFiles ( path . resolve ( 'branding' ) ) ;
196+
197+ return {
198+ define : {
199+ "import.meta.env.BRANDING_LOGO_LIGHT" : JSON . stringify ( `/${ logo_light . filename } ` ) ,
200+ "import.meta.env.BRANDING_LOGO_DARK" : JSON . stringify ( `/${ logo_dark . filename } ` ) ,
201+ }
202+ }
203+ } ,
204+
159205 async configureServer ( server ) {
160206 // For dev
161207 const manifestPath = path . resolve ( "public/manifest.json" ) ;
0 commit comments