You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							224 lines
						
					
					
						
							5.4 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							224 lines
						
					
					
						
							5.4 KiB
						
					
					
				
								#!/usr/bin/env tsx
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * TimeSafari Asset Configuration Generator
							 | 
						|
								 * Generates capacitor-assets configuration files with deterministic outputs
							 | 
						|
								 * Author: Matthew Raymer
							 | 
						|
								 * 
							 | 
						|
								 * Usage: tsx scripts/assets-config.ts
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								import fs from 'fs';
							 | 
						|
								import path from 'path';
							 | 
						|
								import { fileURLToPath } from 'url';
							 | 
						|
								
							 | 
						|
								const __filename = fileURLToPath(import.meta.url);
							 | 
						|
								const __dirname = path.dirname(__filename);
							 | 
						|
								const PROJECT_ROOT = path.dirname(__dirname);
							 | 
						|
								
							 | 
						|
								// TypeScript interfaces for asset configuration
							 | 
						|
								interface AdaptiveIconConfig {
							 | 
						|
								  foreground: string;
							 | 
						|
								  background: string;
							 | 
						|
								  monochrome: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface AndroidIconConfig {
							 | 
						|
								  adaptive: AdaptiveIconConfig;
							 | 
						|
								  target: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface IOSIconConfig {
							 | 
						|
								  padding: number;
							 | 
						|
								  target: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface WebIconConfig {
							 | 
						|
								  target: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface IconConfig {
							 | 
						|
								  source: string;
							 | 
						|
								  android: AndroidIconConfig;
							 | 
						|
								  ios: IOSIconConfig;
							 | 
						|
								  web: WebIconConfig;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface AndroidSplashConfig {
							 | 
						|
								  scale: string;
							 | 
						|
								  target: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface IOSSplashConfig {
							 | 
						|
								  useStoryBoard: boolean;
							 | 
						|
								  target: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface SplashConfig {
							 | 
						|
								  source: string;
							 | 
						|
								  darkSource: string;
							 | 
						|
								  android: AndroidSplashConfig;
							 | 
						|
								  ios: IOSSplashConfig;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface AssetConfig {
							 | 
						|
								  icon: IconConfig;
							 | 
						|
								  splash: SplashConfig;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Generate deterministic capacitor-assets configuration
							 | 
						|
								 * @returns Sorted, stable configuration object
							 | 
						|
								 */
							 | 
						|
								function generateAssetConfig(): AssetConfig {
							 | 
						|
								  const config: AssetConfig = {
							 | 
						|
								    icon: {
							 | 
						|
								      source: "resources/icon.png",
							 | 
						|
								      android: {
							 | 
						|
								        adaptive: {
							 | 
						|
								          foreground: "resources/icon.png",
							 | 
						|
								          background: "#121212",
							 | 
						|
								          monochrome: "resources/icon.png"
							 | 
						|
								        },
							 | 
						|
								        target: "android/app/src/main/res"
							 | 
						|
								      },
							 | 
						|
								      ios: {
							 | 
						|
								        padding: 0,
							 | 
						|
								        target: "ios/App/App/Assets.xcassets/AppIcon.appiconset"
							 | 
						|
								      },
							 | 
						|
								      web: {
							 | 
						|
								        target: "public/img/icons"
							 | 
						|
								      }
							 | 
						|
								    },
							 | 
						|
								    splash: {
							 | 
						|
								      source: "resources/splash.png",
							 | 
						|
								      darkSource: "resources/splash_dark.png",
							 | 
						|
								      android: {
							 | 
						|
								        scale: "cover",
							 | 
						|
								        target: "android/app/src/main/res"
							 | 
						|
								      },
							 | 
						|
								      ios: {
							 | 
						|
								        useStoryBoard: true,
							 | 
						|
								        target: "ios/App/App/Assets.xcassets"
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								  };
							 | 
						|
								
							 | 
						|
								  return sortObjectKeys(config);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Sort object keys recursively for deterministic output
							 | 
						|
								 * @param obj - Object to sort
							 | 
						|
								 * @returns Object with sorted keys
							 | 
						|
								 */
							 | 
						|
								function sortObjectKeys(obj: any): any {
							 | 
						|
								  if (obj === null || typeof obj !== 'object') {
							 | 
						|
								    return obj;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (Array.isArray(obj)) {
							 | 
						|
								    return obj.map(sortObjectKeys);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const sorted: any = {};
							 | 
						|
								  Object.keys(obj)
							 | 
						|
								    .sort()
							 | 
						|
								    .forEach(key => {
							 | 
						|
								      sorted[key] = sortObjectKeys(obj[key]);
							 | 
						|
								    });
							 | 
						|
								
							 | 
						|
								  return sorted;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Validate that required source files exist
							 | 
						|
								 */
							 | 
						|
								function validateSourceFiles(): void {
							 | 
						|
								  const requiredFiles = [
							 | 
						|
								    'resources/icon.png',
							 | 
						|
								    'resources/splash.png',
							 | 
						|
								    'resources/splash_dark.png'
							 | 
						|
								  ];
							 | 
						|
								
							 | 
						|
								  const missingFiles = requiredFiles.filter(file => {
							 | 
						|
								    const filePath = path.join(PROJECT_ROOT, file);
							 | 
						|
								    return !fs.existsSync(filePath);
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  if (missingFiles.length > 0) {
							 | 
						|
								    console.error('❌ Missing required source files:');
							 | 
						|
								    missingFiles.forEach(file => console.error(`   ${file}`));
							 | 
						|
								    process.exit(1);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  console.log('✅ All required source files found');
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Write configuration to file with consistent formatting
							 | 
						|
								 * @param config - Configuration object
							 | 
						|
								 * @param outputPath - Output file path
							 | 
						|
								 */
							 | 
						|
								function writeConfig(config: AssetConfig, outputPath: string): void {
							 | 
						|
								  const jsonString = JSON.stringify(config, null, 2);
							 | 
						|
								  
							 | 
						|
								  // Ensure consistent line endings and no trailing whitespace
							 | 
						|
								  const cleanJson = jsonString
							 | 
						|
								    .split('\n')
							 | 
						|
								    .map(line => line.trimEnd())
							 | 
						|
								    .join('\n') + '\n';
							 | 
						|
								
							 | 
						|
								  fs.writeFileSync(outputPath, cleanJson, 'utf8');
							 | 
						|
								  console.log(`✅ Configuration written to: ${outputPath}`);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Main execution function
							 | 
						|
								 */
							 | 
						|
								function main(): void {
							 | 
						|
								  console.log('🔄 Generating TimeSafari asset configuration...');
							 | 
						|
								  console.log(`📁 Project root: ${PROJECT_ROOT}`);
							 | 
						|
								  console.log(`📅 Generated: ${new Date().toISOString()}`);
							 | 
						|
								
							 | 
						|
								  try {
							 | 
						|
								    // Validate source files exist
							 | 
						|
								    validateSourceFiles();
							 | 
						|
								
							 | 
						|
								    // Generate configuration
							 | 
						|
								    const config = generateAssetConfig();
							 | 
						|
								
							 | 
						|
								    // Ensure config directory exists
							 | 
						|
								    const configDir = path.join(PROJECT_ROOT, 'config', 'assets');
							 | 
						|
								    if (!fs.existsSync(configDir)) {
							 | 
						|
								      fs.mkdirSync(configDir, { recursive: true });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // Write configuration files
							 | 
						|
								    const capacitorAssetsConfigPath = path.join(configDir, 'capacitor-assets.config.json');
							 | 
						|
								    writeConfig(config, capacitorAssetsConfigPath);
							 | 
						|
								
							 | 
						|
								    // Copy to root for capacitor-assets discovery
							 | 
						|
								    const rootConfigPath = path.join(PROJECT_ROOT, 'capacitor-assets.config.json');
							 | 
						|
								    writeConfig(config, rootConfigPath);
							 | 
						|
								
							 | 
						|
								    console.log('🎉 Asset configuration generation completed successfully!');
							 | 
						|
								    console.log('');
							 | 
						|
								    console.log('📋 Next steps:');
							 | 
						|
								    console.log('   1. Review the generated configuration');
							 | 
						|
								    console.log('   2. Commit the configuration files');
							 | 
						|
								    console.log('   3. Run "npm run assets:validate" to verify');
							 | 
						|
								    console.log('   4. Use "npm run build:native" for builds');
							 | 
						|
								
							 | 
						|
								  } catch (error) {
							 | 
						|
								    console.error('❌ Configuration generation failed:', error instanceof Error ? error.message : String(error));
							 | 
						|
								    process.exit(1);
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Run if called directly
							 | 
						|
								if (import.meta.url === `file://${process.argv[1]}`) {
							 | 
						|
								  main();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								export { generateAssetConfig, sortObjectKeys, validateSourceFiles, AssetConfig };
							 | 
						|
								
							 |