From 495a94827a84ee151a9499d84b688be69add4471 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 14 Aug 2025 09:08:06 +0000 Subject: [PATCH] refactor(assets): convert asset management scripts to TypeScript with tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace JavaScript asset scripts with TypeScript equivalents - Install tsx for direct TypeScript execution without compilation - Add proper TypeScript interfaces for AssetConfig and validation - Update package.json scripts to use tsx instead of node - Remove old JavaScript files (assets-config.js, assets-validator.js) - Maintain all existing functionality while improving type safety - Fix module syntax issues that caused build failures on macOS Scripts affected: - assets:config: node → tsx scripts/assets-config.ts - assets:validate: node → tsx scripts/assets-validator.ts Benefits: - Eliminates CommonJS/ES module syntax conflicts - Provides better type safety and IntelliSense - Modernizes development tooling - Ensures cross-platform compatibility --- config/assets/capacitor-assets.config.json | 14 +- package-lock.json | 528 ++++++++++++++++++ package.json | 5 +- .../{assets-config.js => assets-config.ts} | 82 ++- ...ssets-validator.js => assets-validator.ts} | 103 ++-- 5 files changed, 666 insertions(+), 66 deletions(-) rename scripts/{assets-config.js => assets-config.ts} (73%) rename scripts/{assets-validator.js => assets-validator.ts} (64%) diff --git a/config/assets/capacitor-assets.config.json b/config/assets/capacitor-assets.config.json index eb12403e..92bd0414 100644 --- a/config/assets/capacitor-assets.config.json +++ b/config/assets/capacitor-assets.config.json @@ -1,10 +1,9 @@ { "icon": { - "source": "resources/icon.png", "android": { "adaptive": { - "foreground": "resources/icon.png", "background": "#121212", + "foreground": "resources/icon.png", "monochrome": "resources/icon.png" }, "target": "android/app/src/main/res" @@ -13,20 +12,21 @@ "padding": 0, "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" }, + "source": "resources/icon.png", "web": { "target": "public/img/icons" } }, "splash": { - "source": "resources/splash.png", - "darkSource": "resources/splash_dark.png", "android": { "scale": "cover", "target": "android/app/src/main/res" }, + "darkSource": "resources/splash_dark.png", "ios": { - "useStoryBoard": true, - "target": "ios/App/App/Assets.xcassets" - } + "target": "ios/App/App/Assets.xcassets", + "useStoryBoard": true + }, + "source": "resources/splash.png" } } diff --git a/package-lock.json b/package-lock.json index 814a71ad..d6914554 100644 --- a/package-lock.json +++ b/package-lock.json @@ -133,6 +133,7 @@ "rimraf": "^6.0.1", "tailwindcss": "^3.4.1", "ts-jest": "^29.4.0", + "tsx": "^4.20.4", "typescript": "~5.2.2", "vite": "^5.2.0" } @@ -3900,6 +3901,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", @@ -3917,6 +3935,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -3934,6 +3969,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", @@ -17406,6 +17458,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/getenv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", @@ -26798,6 +26863,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve-workspace-root": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz", @@ -29272,6 +29347,459 @@ "dev": true, "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", + "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index 930cc8cf..d961148d 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts", "build:capacitor:sync": "npm run build:capacitor && npx cap sync", "build:native": "vite build && npx cap sync && npx capacitor-assets generate", - "assets:config": "node scripts/assets-config.js", - "assets:validate": "node scripts/assets-validator.js", + "assets:config": "tsx scripts/assets-config.ts", + "assets:validate": "tsx scripts/assets-validator.ts", "assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true", "build:ios": "./scripts/build-ios.sh", "build:ios:dev": "./scripts/build-ios.sh --dev", @@ -245,6 +245,7 @@ "rimraf": "^6.0.1", "tailwindcss": "^3.4.1", "ts-jest": "^29.4.0", + "tsx": "^4.20.4", "typescript": "~5.2.2", "vite": "^5.2.0" } diff --git a/scripts/assets-config.js b/scripts/assets-config.ts similarity index 73% rename from scripts/assets-config.js rename to scripts/assets-config.ts index 926ff1fa..baaadce5 100644 --- a/scripts/assets-config.js +++ b/scripts/assets-config.ts @@ -1,11 +1,11 @@ -#!/usr/bin/env node +#!/usr/bin/env tsx /** * TimeSafari Asset Configuration Generator * Generates capacitor-assets configuration files with deterministic outputs * Author: Matthew Raymer * - * Usage: node scripts/assets-config.js + * Usage: tsx scripts/assets-config.ts */ import fs from 'fs'; @@ -16,12 +16,62 @@ 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 {Object} Sorted, stable configuration object + * @returns Sorted, stable configuration object */ -function generateAssetConfig() { - const config = { +function generateAssetConfig(): AssetConfig { + const config: AssetConfig = { icon: { source: "resources/icon.png", android: { @@ -59,10 +109,10 @@ function generateAssetConfig() { /** * Sort object keys recursively for deterministic output - * @param {Object} obj - Object to sort - * @returns {Object} Object with sorted keys + * @param obj - Object to sort + * @returns Object with sorted keys */ -function sortObjectKeys(obj) { +function sortObjectKeys(obj: any): any { if (obj === null || typeof obj !== 'object') { return obj; } @@ -71,7 +121,7 @@ function sortObjectKeys(obj) { return obj.map(sortObjectKeys); } - const sorted = {}; + const sorted: any = {}; Object.keys(obj) .sort() .forEach(key => { @@ -84,7 +134,7 @@ function sortObjectKeys(obj) { /** * Validate that required source files exist */ -function validateSourceFiles() { +function validateSourceFiles(): void { const requiredFiles = [ 'resources/icon.png', 'resources/splash.png', @@ -107,10 +157,10 @@ function validateSourceFiles() { /** * Write configuration to file with consistent formatting - * @param {Object} config - Configuration object - * @param {string} outputPath - Output file path + * @param config - Configuration object + * @param outputPath - Output file path */ -function writeConfig(config, outputPath) { +function writeConfig(config: AssetConfig, outputPath: string): void { const jsonString = JSON.stringify(config, null, 2); // Ensure consistent line endings and no trailing whitespace @@ -126,7 +176,7 @@ function writeConfig(config, outputPath) { /** * Main execution function */ -function main() { +function main(): void { console.log('🔄 Generating TimeSafari asset configuration...'); console.log(`📁 Project root: ${PROJECT_ROOT}`); console.log(`📅 Generated: ${new Date().toISOString()}`); @@ -161,7 +211,7 @@ function main() { console.log(' 4. Use "npm run build:native" for builds'); } catch (error) { - console.error('❌ Configuration generation failed:', error.message); + console.error('❌ Configuration generation failed:', error instanceof Error ? error.message : String(error)); process.exit(1); } } @@ -171,4 +221,4 @@ if (import.meta.url === `file://${process.argv[1]}`) { main(); } -export { generateAssetConfig, sortObjectKeys, validateSourceFiles }; +export { generateAssetConfig, sortObjectKeys, validateSourceFiles, AssetConfig }; diff --git a/scripts/assets-validator.js b/scripts/assets-validator.ts similarity index 64% rename from scripts/assets-validator.js rename to scripts/assets-validator.ts index fcdbcdff..587a25c7 100644 --- a/scripts/assets-validator.js +++ b/scripts/assets-validator.ts @@ -1,11 +1,11 @@ -#!/usr/bin/env node +#!/usr/bin/env tsx /** * TimeSafari Asset Configuration Validator * Validates capacitor-assets configuration against schema and source files * Author: Matthew Raymer * - * Usage: node scripts/assets-validator.js [config-path] + * Usage: tsx scripts/assets-validator.ts [config-path] */ import fs from 'fs'; @@ -16,50 +16,71 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.dirname(__dirname); +// TypeScript interfaces for validation +interface ValidationError { + message: string; +} + +interface AssetConfig { + icon?: { + source?: string; + android?: { + adaptive?: { + foreground?: string; + background?: string; + monochrome?: string; + }; + }; + }; + splash?: { + source?: string; + darkSource?: string; + }; +} + /** * Load and parse JSON file - * @param {string} filePath - Path to JSON file - * @returns {Object} Parsed JSON object + * @param filePath - Path to JSON file + * @returns Parsed JSON object */ -function loadJsonFile(filePath) { +function loadJsonFile(filePath: string): AssetConfig { try { const content = fs.readFileSync(filePath, 'utf8'); return JSON.parse(content); } catch (error) { - throw new Error(`Failed to load ${filePath}: ${error.message}`); + throw new Error(`Failed to load ${filePath}: ${error instanceof Error ? error.message : String(error)}`); } } /** * Validate configuration against schema - * @param {Object} config - Configuration object to validate - * @param {Object} schema - JSON schema for validation - * @returns {Array} Array of validation errors + * @param config - Configuration object to validate + * @returns Array of validation errors */ -function validateAgainstSchema(config, schema) { - const errors = []; +function validateAgainstSchema(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; // Basic structure validation if (!config.icon || !config.splash) { - errors.push('Configuration must contain both "icon" and "splash" sections'); + errors.push({ message: 'Configuration must contain both "icon" and "splash" sections' }); } // Icon validation if (config.icon) { if (!config.icon.source) { - errors.push('Icon section must contain "source" field'); + errors.push({ message: 'Icon section must contain "source" field' }); } else if (!/^resources\/.*\.(png|svg)$/.test(config.icon.source)) { - errors.push('Icon source must be a PNG or SVG file in resources/ directory'); + errors.push({ message: 'Icon source must be a PNG or SVG file in resources/ directory' }); } // Android adaptive icon validation if (config.icon.android?.adaptive) { const adaptive = config.icon.android.adaptive; if (!adaptive.foreground || !adaptive.background) { - errors.push('Android adaptive icon must have both foreground and background'); + errors.push({ message: 'Android adaptive icon must have both foreground and background' }); } if (adaptive.foreground && !/^resources\/.*\.(png|svg)$/.test(adaptive.foreground)) { - errors.push('Android adaptive foreground must be a PNG or SVG file in resources/ directory'); + errors.push({ message: 'Android adaptive foreground must be a PNG or SVG file in resources/ directory' }); } } } @@ -67,13 +88,13 @@ function validateAgainstSchema(config, schema) { // Splash validation if (config.splash) { if (!config.splash.source) { - errors.push('Splash section must contain "source" field'); + errors.push({ message: 'Splash section must contain "source" field' }); } else if (!/^resources\/.*\.(png|svg)$/.test(config.splash.source)) { - errors.push('Splash source must be a PNG or SVG file in resources/ directory'); + errors.push({ message: 'Splash source must be a PNG or SVG file in resources/ directory' }); } if (config.splash.darkSource && !/^resources\/.*\.(png|svg)$/.test(config.splash.darkSource)) { - errors.push('Dark splash source must be a PNG or SVG file in resources/ directory'); + errors.push({ message: 'Dark splash source must be a PNG or SVG file in resources/ directory' }); } } @@ -82,12 +103,12 @@ function validateAgainstSchema(config, schema) { /** * Validate that source files exist - * @param {Object} config - Configuration object - * @returns {Array} Array of missing file errors + * @param config - Configuration object + * @returns Array of missing file errors */ -function validateSourceFiles(config) { - const errors = []; - const requiredFiles = new Set(); +function validateSourceFiles(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; + const requiredFiles = new Set(); // Collect all required source files if (config.icon?.source) requiredFiles.add(config.icon.source); @@ -100,7 +121,7 @@ function validateSourceFiles(config) { requiredFiles.forEach(file => { const filePath = path.join(PROJECT_ROOT, file); if (!fs.existsSync(filePath)) { - errors.push(`Source file not found: ${file}`); + errors.push({ message: `Source file not found: ${file}` }); } }); @@ -109,12 +130,12 @@ function validateSourceFiles(config) { /** * Validate target directories are writable - * @param {Object} config - Configuration object - * @returns {Array} Array of directory validation errors + * @param config - Configuration object + * @returns Array of directory validation errors */ -function validateTargetDirectories(config) { - const errors = []; - const targetDirs = new Set(); +function validateTargetDirectories(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; + const targetDirs = new Set(); // Collect all target directories if (config.icon?.android?.target) targetDirs.add(config.icon.android.target); @@ -129,9 +150,9 @@ function validateTargetDirectories(config) { const parentDir = path.dirname(dirPath); if (!fs.existsSync(parentDir)) { - errors.push(`Parent directory does not exist: ${parentDir}`); + errors.push({ message: `Parent directory does not exist: ${parentDir}` }); } else if (!fs.statSync(parentDir).isDirectory()) { - errors.push(`Parent path is not a directory: ${parentDir}`); + errors.push({ message: `Parent path is not a directory: ${parentDir}` }); } }); @@ -140,10 +161,10 @@ function validateTargetDirectories(config) { /** * Main validation function - * @param {string} configPath - Path to configuration file - * @returns {boolean} True if validation passes + * @param configPath - Path to configuration file + * @returns True if validation passes */ -function validateConfiguration(configPath) { +function validateConfiguration(configPath: string): boolean { console.log('🔍 Validating TimeSafari asset configuration...'); console.log(`📁 Config file: ${configPath}`); console.log(`📁 Project root: ${PROJECT_ROOT}`); @@ -159,7 +180,7 @@ function validateConfiguration(configPath) { console.log('✅ Schema file loaded successfully'); // Perform validations - const schemaErrors = validateAgainstSchema(config, schema); + const schemaErrors = validateAgainstSchema(config); const fileErrors = validateSourceFiles(config); const dirErrors = validateTargetDirectories(config); @@ -179,13 +200,13 @@ function validateConfiguration(configPath) { } else { console.error('❌ Validation failed with the following errors:'); allErrors.forEach((error, index) => { - console.error(` ${index + 1}. ${error}`); + console.error(` ${index + 1}. ${error.message}`); }); return false; } } catch (error) { - console.error('❌ Validation failed:', error.message); + console.error('❌ Validation failed:', error instanceof Error ? error.message : String(error)); return false; } } @@ -193,7 +214,7 @@ function validateConfiguration(configPath) { /** * Main execution function */ -function main() { +function main(): void { const configPath = process.argv[2] || path.join(PROJECT_ROOT, 'capacitor-assets.config.json'); if (!fs.existsSync(configPath)) { @@ -201,7 +222,7 @@ function main() { console.log(''); console.log('💡 Available options:'); console.log(' - Use default: capacitor-assets.config.json'); - console.log(' - Specify path: node scripts/assets-validator.js path/to/config.json'); + console.log(' - Specify path: tsx scripts/assets-validator.ts path/to/config.json'); console.log(' - Generate config: npm run assets:config'); process.exit(1); } @@ -215,4 +236,4 @@ if (import.meta.url === `file://${process.argv[1]}`) { main(); } -export { validateConfiguration, validateAgainstSchema, validateSourceFiles, validateTargetDirectories }; +export { validateConfiguration, validateAgainstSchema, validateSourceFiles, validateTargetDirectories, AssetConfig };