rothzerg 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/dist/cli.js +949 -0
  2. package/package.json +27 -0
  3. package/readme.md +0 -0
  4. package/templates/_base-website/App.tsx +59 -0
  5. package/templates/_base-website/InitialDataContext.tsx +21 -0
  6. package/templates/_base-website/_gitignore +5 -0
  7. package/templates/_base-website/client.tsx +26 -0
  8. package/templates/_base-website/components/DeepLinkLayout.css +41 -0
  9. package/templates/_base-website/components/DeepLinkLayout.tsx +39 -0
  10. package/templates/_base-website/components/DownloadSection.css +60 -0
  11. package/templates/_base-website/components/DownloadSection.tsx +61 -0
  12. package/templates/_base-website/components/SiteFeaturesSection.tsx +68 -0
  13. package/templates/_base-website/components/SiteFooter.tsx +29 -0
  14. package/templates/_base-website/components/SiteHeroSection.tsx +30 -0
  15. package/templates/_base-website/components/SiteHighlightsSection.tsx +38 -0
  16. package/templates/_base-website/components/SiteLanguageSwitcher.tsx +9 -0
  17. package/templates/_base-website/components/SiteNavigation.tsx +24 -0
  18. package/templates/_base-website/config.ts +78 -0
  19. package/templates/_base-website/data/blog/authors.json +18 -0
  20. package/templates/_base-website/data/blog/categories.json +53 -0
  21. package/templates/_base-website/data/blog/posts.json +29 -0
  22. package/templates/_base-website/data/blog/tags.json +86 -0
  23. package/templates/_base-website/i18n/config.ts +60 -0
  24. package/templates/_base-website/i18n/sections/blog/_index.ts +41 -0
  25. package/templates/_base-website/i18n/sections/blog/ar.ts +24 -0
  26. package/templates/_base-website/i18n/sections/blog/de.ts +24 -0
  27. package/templates/_base-website/i18n/sections/blog/en.ts +24 -0
  28. package/templates/_base-website/i18n/sections/blog/es.ts +24 -0
  29. package/templates/_base-website/i18n/sections/blog/fr.ts +24 -0
  30. package/templates/_base-website/i18n/sections/blog/hi.ts +24 -0
  31. package/templates/_base-website/i18n/sections/blog/id.ts +24 -0
  32. package/templates/_base-website/i18n/sections/blog/it.ts +24 -0
  33. package/templates/_base-website/i18n/sections/blog/ja.ts +24 -0
  34. package/templates/_base-website/i18n/sections/blog/ko.ts +24 -0
  35. package/templates/_base-website/i18n/sections/blog/nl.ts +24 -0
  36. package/templates/_base-website/i18n/sections/blog/pl.ts +24 -0
  37. package/templates/_base-website/i18n/sections/blog/pt.ts +24 -0
  38. package/templates/_base-website/i18n/sections/blog/ru.ts +24 -0
  39. package/templates/_base-website/i18n/sections/blog/sv.ts +24 -0
  40. package/templates/_base-website/i18n/sections/blog/th.ts +24 -0
  41. package/templates/_base-website/i18n/sections/blog/tr.ts +24 -0
  42. package/templates/_base-website/i18n/sections/blog/vi.ts +24 -0
  43. package/templates/_base-website/i18n/sections/blog/zh.ts +24 -0
  44. package/templates/_base-website/i18n/sections/common/_index.ts +41 -0
  45. package/templates/_base-website/i18n/sections/common/ar.ts +29 -0
  46. package/templates/_base-website/i18n/sections/common/de.ts +29 -0
  47. package/templates/_base-website/i18n/sections/common/en.ts +29 -0
  48. package/templates/_base-website/i18n/sections/common/es.ts +29 -0
  49. package/templates/_base-website/i18n/sections/common/fr.ts +29 -0
  50. package/templates/_base-website/i18n/sections/common/hi.ts +29 -0
  51. package/templates/_base-website/i18n/sections/common/id.ts +29 -0
  52. package/templates/_base-website/i18n/sections/common/it.ts +29 -0
  53. package/templates/_base-website/i18n/sections/common/ja.ts +29 -0
  54. package/templates/_base-website/i18n/sections/common/ko.ts +29 -0
  55. package/templates/_base-website/i18n/sections/common/nl.ts +29 -0
  56. package/templates/_base-website/i18n/sections/common/pl.ts +29 -0
  57. package/templates/_base-website/i18n/sections/common/pt.ts +29 -0
  58. package/templates/_base-website/i18n/sections/common/ru.ts +29 -0
  59. package/templates/_base-website/i18n/sections/common/sv.ts +29 -0
  60. package/templates/_base-website/i18n/sections/common/th.ts +29 -0
  61. package/templates/_base-website/i18n/sections/common/tr.ts +29 -0
  62. package/templates/_base-website/i18n/sections/common/vi.ts +29 -0
  63. package/templates/_base-website/i18n/sections/common/zh.ts +29 -0
  64. package/templates/_base-website/i18n/sections/deepLink/_index.ts +41 -0
  65. package/templates/_base-website/i18n/sections/deepLink/ar.ts +14 -0
  66. package/templates/_base-website/i18n/sections/deepLink/de.ts +14 -0
  67. package/templates/_base-website/i18n/sections/deepLink/en.ts +14 -0
  68. package/templates/_base-website/i18n/sections/deepLink/es.ts +14 -0
  69. package/templates/_base-website/i18n/sections/deepLink/fr.ts +14 -0
  70. package/templates/_base-website/i18n/sections/deepLink/hi.ts +14 -0
  71. package/templates/_base-website/i18n/sections/deepLink/id.ts +14 -0
  72. package/templates/_base-website/i18n/sections/deepLink/it.ts +14 -0
  73. package/templates/_base-website/i18n/sections/deepLink/ja.ts +14 -0
  74. package/templates/_base-website/i18n/sections/deepLink/ko.ts +14 -0
  75. package/templates/_base-website/i18n/sections/deepLink/nl.ts +14 -0
  76. package/templates/_base-website/i18n/sections/deepLink/pl.ts +14 -0
  77. package/templates/_base-website/i18n/sections/deepLink/pt.ts +14 -0
  78. package/templates/_base-website/i18n/sections/deepLink/ru.ts +14 -0
  79. package/templates/_base-website/i18n/sections/deepLink/sv.ts +14 -0
  80. package/templates/_base-website/i18n/sections/deepLink/th.ts +14 -0
  81. package/templates/_base-website/i18n/sections/deepLink/tr.ts +14 -0
  82. package/templates/_base-website/i18n/sections/deepLink/vi.ts +14 -0
  83. package/templates/_base-website/i18n/sections/deepLink/zh.ts +14 -0
  84. package/templates/_base-website/i18n/sections/home/_index.ts +41 -0
  85. package/templates/_base-website/i18n/sections/home/ar.ts +85 -0
  86. package/templates/_base-website/i18n/sections/home/de.ts +85 -0
  87. package/templates/_base-website/i18n/sections/home/en.ts +85 -0
  88. package/templates/_base-website/i18n/sections/home/es.ts +85 -0
  89. package/templates/_base-website/i18n/sections/home/fr.ts +85 -0
  90. package/templates/_base-website/i18n/sections/home/hi.ts +85 -0
  91. package/templates/_base-website/i18n/sections/home/id.ts +85 -0
  92. package/templates/_base-website/i18n/sections/home/it.ts +85 -0
  93. package/templates/_base-website/i18n/sections/home/ja.ts +85 -0
  94. package/templates/_base-website/i18n/sections/home/ko.ts +85 -0
  95. package/templates/_base-website/i18n/sections/home/nl.ts +85 -0
  96. package/templates/_base-website/i18n/sections/home/pl.ts +85 -0
  97. package/templates/_base-website/i18n/sections/home/pt.ts +85 -0
  98. package/templates/_base-website/i18n/sections/home/ru.ts +85 -0
  99. package/templates/_base-website/i18n/sections/home/sv.ts +85 -0
  100. package/templates/_base-website/i18n/sections/home/th.ts +85 -0
  101. package/templates/_base-website/i18n/sections/home/tr.ts +85 -0
  102. package/templates/_base-website/i18n/sections/home/vi.ts +85 -0
  103. package/templates/_base-website/i18n/sections/home/zh.ts +85 -0
  104. package/templates/_base-website/i18n/sections/support/_index.ts +41 -0
  105. package/templates/_base-website/i18n/sections/support/ar.ts +37 -0
  106. package/templates/_base-website/i18n/sections/support/de.ts +37 -0
  107. package/templates/_base-website/i18n/sections/support/en.ts +37 -0
  108. package/templates/_base-website/i18n/sections/support/es.ts +37 -0
  109. package/templates/_base-website/i18n/sections/support/fr.ts +37 -0
  110. package/templates/_base-website/i18n/sections/support/hi.ts +37 -0
  111. package/templates/_base-website/i18n/sections/support/id.ts +37 -0
  112. package/templates/_base-website/i18n/sections/support/it.ts +37 -0
  113. package/templates/_base-website/i18n/sections/support/ja.ts +37 -0
  114. package/templates/_base-website/i18n/sections/support/ko.ts +37 -0
  115. package/templates/_base-website/i18n/sections/support/nl.ts +37 -0
  116. package/templates/_base-website/i18n/sections/support/pl.ts +37 -0
  117. package/templates/_base-website/i18n/sections/support/pt.ts +37 -0
  118. package/templates/_base-website/i18n/sections/support/ru.ts +37 -0
  119. package/templates/_base-website/i18n/sections/support/sv.ts +37 -0
  120. package/templates/_base-website/i18n/sections/support/th.ts +37 -0
  121. package/templates/_base-website/i18n/sections/support/tr.ts +37 -0
  122. package/templates/_base-website/i18n/sections/support/vi.ts +37 -0
  123. package/templates/_base-website/i18n/sections/support/zh.ts +37 -0
  124. package/templates/_base-website/i18n/translations.ts +25 -0
  125. package/templates/_base-website/index.ts +460 -0
  126. package/templates/_base-website/pages/404.tsx +35 -0
  127. package/templates/_base-website/pages/blog/author.tsx +97 -0
  128. package/templates/_base-website/pages/blog/category.tsx +89 -0
  129. package/templates/_base-website/pages/blog/index.tsx +81 -0
  130. package/templates/_base-website/pages/blog/post.tsx +110 -0
  131. package/templates/_base-website/pages/blog/tag.tsx +86 -0
  132. package/templates/_base-website/pages/custom-pages/example.tsx +54 -0
  133. package/templates/_base-website/pages/index.tsx +29 -0
  134. package/templates/_base-website/pages/privacy.tsx +45 -0
  135. package/templates/_base-website/pages/support.tsx +154 -0
  136. package/templates/_base-website/pages/terms.tsx +68 -0
  137. package/templates/_base-website/public/images/16.png +0 -0
  138. package/templates/_base-website/public/images/apple-touch-icon.png +0 -0
  139. package/templates/_base-website/public/images/favicon-32x32.png +0 -0
  140. package/templates/_base-website/public/images/favicon.png +0 -0
  141. package/templates/_base-website/public/images/logo_dark.svg +6 -0
  142. package/templates/_base-website/public/images/logo_light.svg +6 -0
  143. package/templates/_base-website/public/images/og-image.png +0 -0
  144. package/templates/_base-website/public/images/screenshots/ar_dashboard.jpg +0 -0
  145. package/templates/_base-website/public/images/screenshots/de_dashboard.jpg +0 -0
  146. package/templates/_base-website/public/images/screenshots/en_dashboard.jpg +0 -0
  147. package/templates/_base-website/public/images/screenshots/es_dashboard.jpg +0 -0
  148. package/templates/_base-website/public/images/screenshots/fr_dashboard.jpg +0 -0
  149. package/templates/_base-website/public/images/screenshots/hi_dashboard.jpg +0 -0
  150. package/templates/_base-website/public/images/screenshots/id_dashboard.jpg +0 -0
  151. package/templates/_base-website/public/images/screenshots/it_dashboard.jpg +0 -0
  152. package/templates/_base-website/public/images/screenshots/ja_dashboard.jpg +0 -0
  153. package/templates/_base-website/public/images/screenshots/ko_dashboard.jpg +0 -0
  154. package/templates/_base-website/public/images/screenshots/nl_dashboard.jpg +0 -0
  155. package/templates/_base-website/public/images/screenshots/pl_dashboard.jpg +0 -0
  156. package/templates/_base-website/public/images/screenshots/pt_dashboard.jpg +0 -0
  157. package/templates/_base-website/public/images/screenshots/ru_dashboard.jpg +0 -0
  158. package/templates/_base-website/public/images/screenshots/sv_dashboard.jpg +0 -0
  159. package/templates/_base-website/public/images/screenshots/th_dashboard.jpg +0 -0
  160. package/templates/_base-website/public/images/screenshots/tr_dashboard.jpg +0 -0
  161. package/templates/_base-website/public/images/screenshots/vi_dashboard.jpg +0 -0
  162. package/templates/_base-website/public/images/screenshots/zh_dashboard.jpg +0 -0
  163. package/templates/_base-website/rothzerg.template.json +63 -0
  164. package/templates/_base-website/styles/404.css +32 -0
  165. package/templates/_base-website/styles/_app.css +131 -0
  166. package/templates/_base-website/styles/_shared.css +194 -0
  167. package/templates/_base-website/styles/index.css +1 -0
  168. package/templates/_base-website/styles/privacy.css +1 -0
  169. package/templates/_base-website/styles/support.css +148 -0
  170. package/templates/_base-website/styles/terms.css +22 -0
  171. package/templates/mobile-app/_gitignore +46 -0
  172. package/templates/mobile-app/analysis_options.yaml +28 -0
  173. package/templates/mobile-app/android/app/build.gradle.kts +56 -0
  174. package/templates/mobile-app/android/app/google-services.json +29 -0
  175. package/templates/mobile-app/android/app/src/debug/AndroidManifest.xml +7 -0
  176. package/templates/mobile-app/android/app/src/main/AndroidManifest.xml +54 -0
  177. package/templates/mobile-app/android/app/src/main/kotlin/com/example/{{projectNameSnake}}/MainActivity.kt +5 -0
  178. package/templates/mobile-app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  179. package/templates/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  180. package/templates/mobile-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  181. package/templates/mobile-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  182. package/templates/mobile-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  183. package/templates/mobile-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  184. package/templates/mobile-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  185. package/templates/mobile-app/android/app/src/main/res/values/styles.xml +18 -0
  186. package/templates/mobile-app/android/app/src/main/res/values-night/styles.xml +18 -0
  187. package/templates/mobile-app/android/app/src/profile/AndroidManifest.xml +7 -0
  188. package/templates/mobile-app/android/build.gradle.kts +24 -0
  189. package/templates/mobile-app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  190. package/templates/mobile-app/android/gradle.properties +2 -0
  191. package/templates/mobile-app/android/settings.gradle.kts +31 -0
  192. package/templates/mobile-app/assets/icons/logo.png +0 -0
  193. package/templates/mobile-app/assets/icons/logo_dark.svg +5 -0
  194. package/templates/mobile-app/assets/icons/logo_light.svg +5 -0
  195. package/templates/mobile-app/assets/lottie/tick.json +1 -0
  196. package/templates/mobile-app/devtools_options.yaml +3 -0
  197. package/templates/mobile-app/ios/Flutter/AppFrameworkInfo.plist +26 -0
  198. package/templates/mobile-app/ios/Flutter/Debug.xcconfig +2 -0
  199. package/templates/mobile-app/ios/Flutter/Release.xcconfig +2 -0
  200. package/templates/mobile-app/ios/Podfile +46 -0
  201. package/templates/mobile-app/ios/Podfile.lock +1807 -0
  202. package/templates/mobile-app/ios/Runner/AppDelegate.swift +16 -0
  203. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  204. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  205. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  206. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  207. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  208. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  209. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  210. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  211. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  212. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  213. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  214. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  215. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  216. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  217. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  218. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  219. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
  220. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  221. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  222. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  223. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
  224. package/templates/mobile-app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
  225. package/templates/mobile-app/ios/Runner/Base.lproj/Main.storyboard +26 -0
  226. package/templates/mobile-app/ios/Runner/GoogleService-Info.plist +30 -0
  227. package/templates/mobile-app/ios/Runner/Info.plist +70 -0
  228. package/templates/mobile-app/ios/Runner/Runner-Bridging-Header.h +1 -0
  229. package/templates/mobile-app/ios/Runner.xcodeproj/project.pbxproj +772 -0
  230. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  231. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  232. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  233. package/templates/mobile-app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
  234. package/templates/mobile-app/ios/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  235. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  236. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  237. package/templates/mobile-app/ios/RunnerTests/RunnerTests.swift +12 -0
  238. package/templates/mobile-app/l10n.yaml +5 -0
  239. package/templates/mobile-app/lib/core/config/app_config.dart +76 -0
  240. package/templates/mobile-app/lib/core/constants/app_colors.dart +13 -0
  241. package/templates/mobile-app/lib/core/constants/app_constants.dart +123 -0
  242. package/templates/mobile-app/lib/core/constants/countries.dart +199 -0
  243. package/templates/mobile-app/lib/core/constants/keys.dart +46 -0
  244. package/templates/mobile-app/lib/core/constants/languages.dart +51 -0
  245. package/templates/mobile-app/lib/core/services/admin_service.dart +271 -0
  246. package/templates/mobile-app/lib/core/services/analytics_service.dart +296 -0
  247. package/templates/mobile-app/lib/core/services/auth_service.dart +185 -0
  248. package/templates/mobile-app/lib/core/services/device_service.dart +228 -0
  249. package/templates/mobile-app/lib/core/services/firebase_service.dart +490 -0
  250. package/templates/mobile-app/lib/core/services/in_app_messaging_service.dart +166 -0
  251. package/templates/mobile-app/lib/core/services/logging_service.dart +451 -0
  252. package/templates/mobile-app/lib/core/services/messaging_service.dart +460 -0
  253. package/templates/mobile-app/lib/core/services/remote_config_service.dart +167 -0
  254. package/templates/mobile-app/lib/core/services/revenue_cat_service.dart +364 -0
  255. package/templates/mobile-app/lib/core/services/storage_service.dart +313 -0
  256. package/templates/mobile-app/lib/core/theme/dark_theme.dart +295 -0
  257. package/templates/mobile-app/lib/core/theme/light_theme.dart +296 -0
  258. package/templates/mobile-app/lib/core/utils/formatters.dart +236 -0
  259. package/templates/mobile-app/lib/core/utils/image_picker_helper.dart +78 -0
  260. package/templates/mobile-app/lib/core/utils/validators.dart +292 -0
  261. package/templates/mobile-app/lib/firebase_options.dart +19 -0
  262. package/templates/mobile-app/lib/l10n/app_en.arb +1371 -0
  263. package/templates/mobile-app/lib/l10n/app_localizations.dart +2042 -0
  264. package/templates/mobile-app/lib/l10n/app_localizations_en.dart +1033 -0
  265. package/templates/mobile-app/lib/main.dart +152 -0
  266. package/templates/mobile-app/lib/models/device_model.dart +90 -0
  267. package/templates/mobile-app/lib/models/news_banner.dart +53 -0
  268. package/templates/mobile-app/lib/models/user_model.dart +120 -0
  269. package/templates/mobile-app/lib/providers/admin_provider.dart +67 -0
  270. package/templates/mobile-app/lib/providers/auth_provider.dart +328 -0
  271. package/templates/mobile-app/lib/providers/remote_config_provider.dart +81 -0
  272. package/templates/mobile-app/lib/providers/subscription_provider.dart +67 -0
  273. package/templates/mobile-app/lib/providers/theme_provider.dart +51 -0
  274. package/templates/mobile-app/lib/screens/admin/admin_panel_screen.dart +436 -0
  275. package/templates/mobile-app/lib/screens/admin/admin_panel_user_details_screen.dart +942 -0
  276. package/templates/mobile-app/lib/screens/admin/admin_panel_users_screen.dart +344 -0
  277. package/templates/mobile-app/lib/screens/auth/auth_home_screen.dart +266 -0
  278. package/templates/mobile-app/lib/screens/auth/forgot_password_screen.dart +214 -0
  279. package/templates/mobile-app/lib/screens/auth/login_screen.dart +312 -0
  280. package/templates/mobile-app/lib/screens/auth/profile_setup_screen.dart +157 -0
  281. package/templates/mobile-app/lib/screens/auth/register_screen.dart +288 -0
  282. package/templates/mobile-app/lib/screens/developer/developer_panel_screen.dart +436 -0
  283. package/templates/mobile-app/lib/screens/developer/remote_config_screen.dart +315 -0
  284. package/templates/mobile-app/lib/screens/developer/remote_config_test_screen.dart +461 -0
  285. package/templates/mobile-app/lib/screens/home/home_screen.dart +270 -0
  286. package/templates/mobile-app/lib/screens/legal/privacy_policy_screen.dart +63 -0
  287. package/templates/mobile-app/lib/screens/legal/terms_screen.dart +82 -0
  288. package/templates/mobile-app/lib/screens/main_navigation_screen.dart +85 -0
  289. package/templates/mobile-app/lib/screens/premium/subscription_screen.dart +516 -0
  290. package/templates/mobile-app/lib/screens/profile/edit_profile_screen.dart +246 -0
  291. package/templates/mobile-app/lib/screens/settings/about_app_screen.dart +200 -0
  292. package/templates/mobile-app/lib/screens/settings/cache_manager_screen.dart +400 -0
  293. package/templates/mobile-app/lib/screens/settings/change_password_screen.dart +269 -0
  294. package/templates/mobile-app/lib/screens/settings/delete_account_screen.dart +261 -0
  295. package/templates/mobile-app/lib/screens/settings/settings_screen.dart +400 -0
  296. package/templates/mobile-app/lib/screens/splash/splash_screen.dart +125 -0
  297. package/templates/mobile-app/lib/widgets/app_button.dart +255 -0
  298. package/templates/mobile-app/lib/widgets/app_drawer.dart +207 -0
  299. package/templates/mobile-app/lib/widgets/bottom_sheet_selector.dart +79 -0
  300. package/templates/mobile-app/lib/widgets/button_banner.dart +177 -0
  301. package/templates/mobile-app/lib/widgets/country_picker.dart +169 -0
  302. package/templates/mobile-app/lib/widgets/custom_app_bar.dart +30 -0
  303. package/templates/mobile-app/lib/widgets/email_verification_banner.dart +163 -0
  304. package/templates/mobile-app/lib/widgets/empty_screen.dart +42 -0
  305. package/templates/mobile-app/lib/widgets/language_picker.dart +145 -0
  306. package/templates/mobile-app/lib/widgets/news_banner_widget.dart +264 -0
  307. package/templates/mobile-app/lib/widgets/remote_config/remote_config_widgets.dart +294 -0
  308. package/templates/mobile-app/lib/widgets/section_header.dart +23 -0
  309. package/templates/mobile-app/lib/widgets/success_overlay.dart +117 -0
  310. package/templates/mobile-app/lib/widgets/top_notification.dart +263 -0
  311. package/templates/mobile-app/lib/widgets/user_avatar_picker.dart +58 -0
  312. package/templates/mobile-app/pubspec.yaml +97 -0
  313. package/templates/mobile-app/rothzerg.template.json +43 -0
  314. package/templates/web-app/_gitignore +4 -0
  315. package/templates/web-app/index.html +12 -0
  316. package/templates/web-app/package.json +23 -0
  317. package/templates/web-app/rothzerg.template.json +41 -0
  318. package/templates/web-app/src/App.css +6 -0
  319. package/templates/web-app/src/App.tsx +12 -0
  320. package/templates/web-app/src/index.css +14 -0
  321. package/templates/web-app/src/main.tsx +10 -0
  322. package/templates/web-app/tsconfig.app.json +20 -0
  323. package/templates/web-app/tsconfig.json +7 -0
  324. package/templates/web-app/tsconfig.node.json +18 -0
  325. package/templates/web-app/vite.config.ts +6 -0
  326. package/templates/website-blog/overrides/config.ts +76 -0
  327. package/templates/website-blog/overrides/pages/index.tsx +63 -0
  328. package/templates/website-blog/rothzerg.template.json +29 -0
  329. package/templates/website-business/overrides/App.tsx +49 -0
  330. package/templates/website-business/overrides/components/SiteHeroSection.tsx +33 -0
  331. package/templates/website-business/overrides/config.ts +76 -0
  332. package/templates/website-business/overrides/pages/about.tsx +50 -0
  333. package/templates/website-business/overrides/pages/contact.tsx +61 -0
  334. package/templates/website-business/overrides/pages/index.tsx +37 -0
  335. package/templates/website-business/overrides/pages/services.tsx +70 -0
  336. package/templates/website-business/rothzerg.template.json +18 -0
  337. package/templates/website-mobile/overrides/App.tsx +62 -0
  338. package/templates/website-mobile/overrides/components/SiteHeroSection.tsx +18 -0
  339. package/templates/website-mobile/overrides/config.ts +76 -0
  340. package/templates/website-mobile/overrides/pages/index.tsx +34 -0
  341. package/templates/website-mobile/rothzerg.template.json +64 -0
package/dist/cli.js ADDED
@@ -0,0 +1,949 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import path8 from "path";
5
+ import { fileURLToPath } from "url";
6
+ import fs7 from "fs";
7
+ import * as p4 from "@clack/prompts";
8
+ import pc3 from "picocolors";
9
+
10
+ // src/commands/create.ts
11
+ import path6 from "path";
12
+ import * as p2 from "@clack/prompts";
13
+ import pc from "picocolors";
14
+
15
+ // src/engine/discovery.ts
16
+ import fs2 from "fs";
17
+ import path2 from "path";
18
+
19
+ // src/utils.ts
20
+ import fs from "fs";
21
+ import path from "path";
22
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
23
+ ".ts",
24
+ ".tsx",
25
+ ".js",
26
+ ".jsx",
27
+ ".json",
28
+ ".html",
29
+ ".css",
30
+ ".scss",
31
+ ".md",
32
+ ".txt",
33
+ ".yaml",
34
+ ".yml",
35
+ ".toml",
36
+ ".xml",
37
+ ".svg",
38
+ ".dart",
39
+ ".gradle",
40
+ ".kt",
41
+ ".swift",
42
+ ".plist",
43
+ ".sh",
44
+ ".env",
45
+ ".gitignore",
46
+ ".npmrc",
47
+ ".prettierrc",
48
+ ".eslintrc",
49
+ ".editorconfig"
50
+ ]);
51
+ function isTextFile(filePath) {
52
+ const ext = path.extname(filePath).toLowerCase();
53
+ const base = path.basename(filePath).toLowerCase();
54
+ const textBasenames = /* @__PURE__ */ new Set([
55
+ "makefile",
56
+ "dockerfile",
57
+ "procfile",
58
+ "rakefile",
59
+ ".env",
60
+ ".gitignore",
61
+ ".npmrc",
62
+ ".prettierrc"
63
+ ]);
64
+ return TEXT_EXTENSIONS.has(ext) || textBasenames.has(base);
65
+ }
66
+ function applyFileRename(relativePath) {
67
+ const parts = relativePath.split(path.sep);
68
+ const renamed = parts.map((part) => {
69
+ if (part.startsWith("_") && !part.startsWith("__")) {
70
+ return "." + part.slice(1);
71
+ }
72
+ return part;
73
+ });
74
+ return renamed.join(path.sep);
75
+ }
76
+ function listFilesRecursive(dir) {
77
+ const results = [];
78
+ function walk(current, relative) {
79
+ const entries = fs.readdirSync(current, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ const entryRelative = relative ? path.join(relative, entry.name) : entry.name;
82
+ const entryAbsolute = path.join(current, entry.name);
83
+ if (entry.isDirectory()) {
84
+ walk(entryAbsolute, entryRelative);
85
+ } else {
86
+ results.push(entryRelative);
87
+ }
88
+ }
89
+ }
90
+ walk(dir, "");
91
+ return results;
92
+ }
93
+ function readJson(filePath) {
94
+ try {
95
+ const content = fs.readFileSync(filePath, "utf-8");
96
+ return JSON.parse(content);
97
+ } catch (err) {
98
+ throw new Error(`Failed to read JSON at ${filePath}: ${err.message}`);
99
+ }
100
+ }
101
+ function writeJson(filePath, data) {
102
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
103
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
104
+ }
105
+ function exists(p5) {
106
+ return fs.existsSync(p5);
107
+ }
108
+ function ensureDir(dir) {
109
+ fs.mkdirSync(dir, { recursive: true });
110
+ }
111
+ function matchesGlob(pattern, filePath) {
112
+ const normalizedFile = filePath.replace(/\\/g, "/");
113
+ const normalizedPattern = pattern.replace(/\\/g, "/");
114
+ const regexStr = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "__STAR__").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*").replace(/__STAR__/g, "\\*");
115
+ const regex = new RegExp(`^${regexStr}$`);
116
+ return regex.test(normalizedFile);
117
+ }
118
+ function matchesAnyGlob(patterns, filePath) {
119
+ return patterns.some((p5) => matchesGlob(p5, filePath));
120
+ }
121
+
122
+ // src/engine/discovery.ts
123
+ var TEMPLATE_CONFIG_FILENAME = "rothzerg.template.json";
124
+ function discoverTemplates(templatesDir) {
125
+ if (!fs2.existsSync(templatesDir)) {
126
+ return [];
127
+ }
128
+ const entries = fs2.readdirSync(templatesDir, { withFileTypes: true });
129
+ const templates = [];
130
+ for (const entry of entries) {
131
+ if (!entry.isDirectory()) continue;
132
+ if (entry.name.startsWith("_")) continue;
133
+ const configPath = path2.join(templatesDir, entry.name, TEMPLATE_CONFIG_FILENAME);
134
+ if (!fs2.existsSync(configPath)) continue;
135
+ try {
136
+ const config = readJson(configPath);
137
+ templates.push(config);
138
+ } catch {
139
+ }
140
+ }
141
+ templates.sort((a, b) => {
142
+ if (a.category !== b.category) return a.category.localeCompare(b.category);
143
+ const aOrder = a.order ?? 999;
144
+ const bOrder = b.order ?? 999;
145
+ if (aOrder !== bOrder) return aOrder - bOrder;
146
+ return a.displayName.localeCompare(b.displayName);
147
+ });
148
+ return templates;
149
+ }
150
+ function loadTemplateConfig(templatesDir, name) {
151
+ const configPath = path2.join(templatesDir, name, TEMPLATE_CONFIG_FILENAME);
152
+ if (!fs2.existsSync(configPath)) {
153
+ throw new Error(`Template "${name}" not found at ${configPath}`);
154
+ }
155
+ return readJson(configPath);
156
+ }
157
+ function getTemplateDir(templatesDir, name) {
158
+ return path2.join(templatesDir, name);
159
+ }
160
+ function groupByCategory(templates) {
161
+ const groups = /* @__PURE__ */ new Map();
162
+ for (const template of templates) {
163
+ const group = groups.get(template.category) ?? [];
164
+ group.push(template);
165
+ groups.set(template.category, group);
166
+ }
167
+ return groups;
168
+ }
169
+
170
+ // src/engine/inheritance.ts
171
+ import path3 from "path";
172
+ import fs3 from "fs";
173
+ function resolveTemplate(templatesDir, templateConfig) {
174
+ if (!templateConfig.extends) {
175
+ const dir = getTemplateDir(templatesDir, templateConfig.name);
176
+ const files2 = buildFileList(dir, dir);
177
+ return { config: templateConfig, files: files2, dir };
178
+ }
179
+ const baseConfig = loadTemplateConfig(templatesDir, templateConfig.extends);
180
+ const baseDir = getTemplateDir(templatesDir, templateConfig.extends);
181
+ const childDir = getTemplateDir(templatesDir, templateConfig.name);
182
+ const overridesDir = path3.join(childDir, "overrides");
183
+ const baseFiles = buildFileList(baseDir, baseDir);
184
+ const fileMap = /* @__PURE__ */ new Map();
185
+ for (const f of baseFiles) {
186
+ fileMap.set(f.relativePath, f);
187
+ }
188
+ if (fs3.existsSync(overridesDir)) {
189
+ const overrideFiles = buildFileList(overridesDir, overridesDir);
190
+ for (const f of overrideFiles) {
191
+ fileMap.set(f.relativePath, f);
192
+ }
193
+ }
194
+ const files = Array.from(fileMap.values());
195
+ const mergedConfig = mergeConfigs(baseConfig, templateConfig);
196
+ return { config: mergedConfig, files, dir: baseDir };
197
+ }
198
+ function buildFileList(dir, rootDir) {
199
+ const relativePaths = listFilesRecursive(dir);
200
+ return relativePaths.filter((rel) => {
201
+ const normalized = rel.replace(/\\/g, "/");
202
+ return normalized !== "rothzerg.template.json" && !normalized.startsWith("overrides/");
203
+ }).map((relativePath) => ({
204
+ src: path3.join(rootDir, relativePath),
205
+ relativePath
206
+ }));
207
+ }
208
+ function mergeConfigs(base, child) {
209
+ return {
210
+ // Scalar fields: child wins
211
+ name: child.name,
212
+ displayName: child.displayName,
213
+ description: child.description,
214
+ icon: child.icon ?? base.icon,
215
+ category: child.category,
216
+ order: child.order ?? base.order,
217
+ extends: child.extends,
218
+ // Prompts: concatenated
219
+ prompts: [...base.prompts ?? [], ...child.prompts ?? []],
220
+ // Derived: merged, child overrides
221
+ derived: { ...base.derived ?? {}, ...child.derived ?? {} },
222
+ // Target: child wins, falls back to base
223
+ target: child.target ?? base.target,
224
+ // ConditionalFiles: merged, child overrides
225
+ conditionalFiles: {
226
+ ...base.conditionalFiles ?? {},
227
+ ...child.conditionalFiles ?? {}
228
+ },
229
+ // Hooks: concatenated
230
+ hooks: {
231
+ postCreate: [
232
+ ...base.hooks?.postCreate ?? [],
233
+ ...child.hooks?.postCreate ?? []
234
+ ],
235
+ postUpgrade: [
236
+ ...base.hooks?.postUpgrade ?? [],
237
+ ...child.hooks?.postUpgrade ?? []
238
+ ]
239
+ },
240
+ // Upgrade: child wins
241
+ upgrade: child.upgrade ?? base.upgrade,
242
+ // NextSteps: child wins
243
+ nextSteps: child.nextSteps ?? base.nextSteps
244
+ };
245
+ }
246
+
247
+ // src/engine/prompts.ts
248
+ import * as p from "@clack/prompts";
249
+
250
+ // src/engine/placeholders.ts
251
+ var FILTERS = {
252
+ pascalCase: (v) => v.replace(/[-_.\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toUpperCase()),
253
+ camelCase: (v) => v.replace(/[-_.\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase()),
254
+ snakeCase: (v) => v.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[-.\s]+/g, "_").toLowerCase(),
255
+ kebabCase: (v) => v.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[_.\s]+/g, "-").toLowerCase(),
256
+ titleCase: (v) => {
257
+ const stripped = v.replace(/\.[a-z]{2,}$/i, "");
258
+ return stripped.charAt(0).toUpperCase() + stripped.slice(1).toLowerCase();
259
+ },
260
+ upperCase: (v) => v.toUpperCase(),
261
+ lowerCase: (v) => v.toLowerCase(),
262
+ stripTld: (v) => v.replace(/\.[a-z]{2,}$/i, "")
263
+ };
264
+ function applyFilters(value, filterNames) {
265
+ return filterNames.reduce((v, name) => {
266
+ const filter = FILTERS[name.trim()];
267
+ if (!filter) throw new Error(`Unknown placeholder filter: "${name.trim()}"`);
268
+ return filter(v);
269
+ }, value);
270
+ }
271
+ function resolveExpr(expr, vars, configVars) {
272
+ const parts = expr.split("|").map((p5) => p5.trim());
273
+ const [head, ...filters] = parts;
274
+ let value;
275
+ if (head.startsWith("config:")) {
276
+ const key = head.slice("config:".length);
277
+ value = configVars[key] ?? "";
278
+ } else if (head.startsWith("builtIn:")) {
279
+ const key = head.slice("builtIn:".length);
280
+ value = resolveBuiltIn(key);
281
+ } else {
282
+ const raw = vars[head];
283
+ if (raw === void 0) return `{{${expr}}}`;
284
+ value = String(raw);
285
+ }
286
+ return filters.length > 0 ? applyFilters(value, filters) : value;
287
+ }
288
+ function resolveBuiltIn(key) {
289
+ switch (key) {
290
+ case "year":
291
+ return String((/* @__PURE__ */ new Date()).getFullYear());
292
+ case "timestamp":
293
+ return (/* @__PURE__ */ new Date()).toISOString();
294
+ case "date":
295
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
296
+ default:
297
+ return "";
298
+ }
299
+ }
300
+ var PLACEHOLDER_RE = /\{\{([^}]+)\}\}/g;
301
+ function interpolate(template, vars, configVars = {}) {
302
+ return template.replace(PLACEHOLDER_RE, (match, expr) => {
303
+ try {
304
+ return resolveExpr(expr.trim(), vars, configVars);
305
+ } catch {
306
+ return match;
307
+ }
308
+ });
309
+ }
310
+ function computeDerived(derived, vars, configVars = {}) {
311
+ const result = { ...vars };
312
+ for (const [key, template] of Object.entries(derived)) {
313
+ result[key] = interpolate(template, result, configVars);
314
+ }
315
+ return result;
316
+ }
317
+ function processFileContent(content, vars, configVars = {}) {
318
+ return interpolate(content, vars, configVars);
319
+ }
320
+ function evaluateCondition(condition, vars, configVars = {}) {
321
+ const trimmed = condition.trim();
322
+ if (trimmed.startsWith("!")) {
323
+ const inner = trimmed.slice(1).trim();
324
+ return !evaluateCondition(inner, vars, configVars);
325
+ }
326
+ const resolved = interpolate(trimmed, vars, configVars);
327
+ if (resolved === "true" || resolved === "1" || resolved === "yes") return true;
328
+ if (resolved === "false" || resolved === "0" || resolved === "no" || resolved === "") return false;
329
+ return resolved.length > 0;
330
+ }
331
+
332
+ // src/engine/prompts.ts
333
+ async function runPrompts(prompts, prefilled = {}, configVars = {}) {
334
+ const vars = { ...prefilled };
335
+ for (const prompt of prompts) {
336
+ if (vars[prompt.name] !== void 0) continue;
337
+ const answer = await runSinglePrompt(prompt, vars, configVars);
338
+ if (p.isCancel(answer)) {
339
+ p.cancel("Operation cancelled.");
340
+ process.exit(0);
341
+ }
342
+ vars[prompt.name] = answer;
343
+ }
344
+ return vars;
345
+ }
346
+ async function runSinglePrompt(prompt, currentVars, configVars) {
347
+ const message = prompt.message;
348
+ switch (prompt.type) {
349
+ case "text": {
350
+ const defaultValue = resolveDefault(prompt.default, currentVars, configVars);
351
+ return p.text({
352
+ message,
353
+ placeholder: prompt.placeholder,
354
+ defaultValue: typeof defaultValue === "string" ? defaultValue : void 0,
355
+ validate: prompt.validate ? (value) => {
356
+ const pattern = new RegExp(prompt.validate);
357
+ if (!pattern.test(value)) {
358
+ return prompt.validateMessage ?? `Must match pattern: ${prompt.validate}`;
359
+ }
360
+ return void 0;
361
+ } : void 0
362
+ });
363
+ }
364
+ case "confirm": {
365
+ const defaultValue = resolveDefault(prompt.default, currentVars, configVars);
366
+ return p.confirm({
367
+ message,
368
+ initialValue: typeof defaultValue === "boolean" ? defaultValue : prompt.default
369
+ });
370
+ }
371
+ case "select": {
372
+ const options = (prompt.options ?? []).map((opt) => ({
373
+ value: opt.value,
374
+ label: opt.label,
375
+ hint: opt.hint
376
+ }));
377
+ return p.select({ message, options });
378
+ }
379
+ case "multiselect": {
380
+ const options = (prompt.options ?? []).map((opt) => ({
381
+ value: opt.value,
382
+ label: opt.label,
383
+ hint: opt.hint
384
+ }));
385
+ return p.multiselect({
386
+ message,
387
+ options,
388
+ required: prompt.required ?? false
389
+ });
390
+ }
391
+ default:
392
+ throw new Error(`Unknown prompt type: "${prompt.type}"`);
393
+ }
394
+ }
395
+ function resolveDefault(defaultValue, vars, configVars) {
396
+ if (defaultValue === void 0) return void 0;
397
+ if (typeof defaultValue === "boolean") return defaultValue;
398
+ if (Array.isArray(defaultValue)) return defaultValue;
399
+ return interpolate(defaultValue, vars, configVars);
400
+ }
401
+ function parseCliFlags(args) {
402
+ const vars = {};
403
+ for (let i = 0; i < args.length; i++) {
404
+ const arg = args[i];
405
+ if (!arg.startsWith("--")) continue;
406
+ const withoutDashes = arg.slice(2);
407
+ if (withoutDashes.includes("=")) {
408
+ const [key, ...valueParts] = withoutDashes.split("=");
409
+ vars[key] = coerceValue(valueParts.join("="));
410
+ } else {
411
+ const next = args[i + 1];
412
+ if (next && !next.startsWith("--")) {
413
+ vars[withoutDashes] = coerceValue(next);
414
+ i++;
415
+ }
416
+ }
417
+ }
418
+ return vars;
419
+ }
420
+ function coerceValue(value) {
421
+ if (value === "true") return true;
422
+ if (value === "false") return false;
423
+ return value;
424
+ }
425
+
426
+ // src/engine/conditions.ts
427
+ function filterConditionalFiles(files, conditionalFiles, vars, configVars = {}) {
428
+ if (!conditionalFiles || Object.keys(conditionalFiles).length === 0) {
429
+ return files;
430
+ }
431
+ return files.filter((file) => {
432
+ const normalizedPath = file.relativePath.replace(/\\/g, "/");
433
+ for (const [glob, condition] of Object.entries(conditionalFiles)) {
434
+ if (matchesGlob(glob, normalizedPath)) {
435
+ const include = evaluateCondition(condition, vars, configVars);
436
+ if (!include) return false;
437
+ }
438
+ }
439
+ return true;
440
+ });
441
+ }
442
+
443
+ // src/engine/copy.ts
444
+ import fs4 from "fs";
445
+ import path4 from "path";
446
+ function copyFiles(files, targetDir, vars, configVars = {}) {
447
+ const copied = [];
448
+ const skipped = [];
449
+ ensureDir(targetDir);
450
+ for (const file of files) {
451
+ const renamedRelative = applyFileRename(file.relativePath);
452
+ const destPath = path4.join(targetDir, renamedRelative);
453
+ ensureDir(path4.dirname(destPath));
454
+ if (isTextFile(file.src)) {
455
+ try {
456
+ const content = fs4.readFileSync(file.src, "utf-8");
457
+ const processed = processFileContent(content, vars, configVars);
458
+ fs4.writeFileSync(destPath, processed, "utf-8");
459
+ copied.push(renamedRelative);
460
+ } catch {
461
+ fs4.copyFileSync(file.src, destPath);
462
+ copied.push(renamedRelative);
463
+ }
464
+ } else {
465
+ fs4.copyFileSync(file.src, destPath);
466
+ copied.push(renamedRelative);
467
+ }
468
+ }
469
+ return { copied, skipped };
470
+ }
471
+ function copyFileWithTransform(src, dest, vars, configVars = {}) {
472
+ ensureDir(path4.dirname(dest));
473
+ if (isTextFile(src)) {
474
+ const content = fs4.readFileSync(src, "utf-8");
475
+ const processed = processFileContent(content, vars, configVars);
476
+ fs4.writeFileSync(dest, processed, "utf-8");
477
+ } else {
478
+ fs4.copyFileSync(src, dest);
479
+ }
480
+ }
481
+
482
+ // src/engine/hooks.ts
483
+ import { spawnSync } from "child_process";
484
+ async function runHooks(hooks, context) {
485
+ const results = [];
486
+ for (const hook of hooks) {
487
+ if (hook.when) {
488
+ const shouldRun = evaluateCondition(hook.when, context.vars, context.configVars);
489
+ if (!shouldRun) continue;
490
+ }
491
+ const result = await runHook(hook, context);
492
+ results.push(result);
493
+ }
494
+ return results;
495
+ }
496
+ async function runHook(hook, context) {
497
+ const hookName = interpolate(hook.run, context.vars, context.configVars);
498
+ try {
499
+ if (hook.builtin) {
500
+ const builtin = context.builtins[hook.run];
501
+ if (!builtin) {
502
+ throw new Error(`Unknown built-in hook: "${hook.run}"`);
503
+ }
504
+ await builtin(context);
505
+ return { hook: hookName, success: true };
506
+ }
507
+ const cwd = hook.cwd ? interpolate(hook.cwd, context.vars, context.configVars) : context.projectDir;
508
+ const [cmd, ...args] = hookName.split(" ");
509
+ const result = spawnSync(cmd, args, {
510
+ cwd,
511
+ stdio: "inherit",
512
+ shell: true
513
+ });
514
+ if (result.status !== 0) {
515
+ throw new Error(`Command exited with code ${result.status ?? "unknown"}`);
516
+ }
517
+ return { hook: hookName, success: true };
518
+ } catch (err) {
519
+ return { hook: hookName, success: false, error: err.message };
520
+ }
521
+ }
522
+
523
+ // src/hooks/register-website.ts
524
+ import fs5 from "fs";
525
+ import path5 from "path";
526
+ var registerWebsite = (context) => {
527
+ const { vars, configVars } = context;
528
+ const websitesRoot = configVars["websitesRoot"];
529
+ if (!websitesRoot) {
530
+ throw new Error(
531
+ 'register-website hook requires "websitesRoot" in global config. Set it in ~/.rothzerg/config.json or via --config flag.'
532
+ );
533
+ }
534
+ const indexPath = path5.join(websitesRoot, "app", "index.ts");
535
+ if (!fs5.existsSync(indexPath)) {
536
+ throw new Error(`Could not find app/index.ts at: ${indexPath}`);
537
+ }
538
+ const domain = String(vars["domain"] ?? "");
539
+ const handlerName = String(vars["handlerName"] ?? `handleRequest`);
540
+ const localDomain = String(vars["localDomain"] ?? `${domain.replace(/\.[^.]+$/, "")}.localhost.com`);
541
+ let content = fs5.readFileSync(indexPath, "utf-8");
542
+ const importStatement = `import { handleRequest as ${handlerName} } from "./websites/${domain}";`;
543
+ if (!content.includes(importStatement)) {
544
+ const lastImportMatch = [...content.matchAll(/^import .+;?$/gm)];
545
+ if (lastImportMatch.length > 0) {
546
+ const lastImport = lastImportMatch[lastImportMatch.length - 1];
547
+ const insertAt = lastImport.index + lastImport[0].length;
548
+ content = content.slice(0, insertAt) + "\n" + importStatement + content.slice(insertAt);
549
+ } else {
550
+ content = importStatement + "\n" + content;
551
+ }
552
+ }
553
+ const websiteEntry = ` {
554
+ location: "${domain}",
555
+ domains: ["${localDomain}", "dev.${domain}", "www.${domain}", "${domain}"],
556
+ handleRequest: (hostname: string, pathname: string, req: Request) => ${handlerName}(hostname, pathname),
557
+ },`;
558
+ if (!content.includes(`location: "${domain}"`)) {
559
+ const arrayMatch = content.match(/const websites[^=]*=\s*\[/);
560
+ if (arrayMatch && arrayMatch.index !== void 0) {
561
+ const startIdx = arrayMatch.index + arrayMatch[0].length;
562
+ let depth = 1;
563
+ let i = startIdx;
564
+ while (i < content.length && depth > 0) {
565
+ if (content[i] === "[") depth++;
566
+ else if (content[i] === "]") depth--;
567
+ i++;
568
+ }
569
+ const closingIdx = i - 1;
570
+ content = content.slice(0, closingIdx) + "\n" + websiteEntry + "\n" + content.slice(closingIdx);
571
+ } else {
572
+ throw new Error(
573
+ "Could not find `const websites = [` array in app/index.ts. Please manually add the entry."
574
+ );
575
+ }
576
+ }
577
+ fs5.writeFileSync(indexPath, content, "utf-8");
578
+ };
579
+
580
+ // src/commands/create.ts
581
+ async function createCommand(options) {
582
+ const { templatesDir, globalConfig, cliFlags, version } = options;
583
+ const configVars = globalConfig;
584
+ const allTemplates = discoverTemplates(templatesDir);
585
+ if (allTemplates.length === 0) {
586
+ p2.log.error("No templates found in " + templatesDir);
587
+ process.exit(1);
588
+ }
589
+ let templateConfig;
590
+ if (options.templateName) {
591
+ try {
592
+ templateConfig = loadTemplateConfig(templatesDir, options.templateName);
593
+ } catch {
594
+ p2.log.error(`Template "${options.templateName}" not found.`);
595
+ p2.log.info(`Available templates: ${allTemplates.map((t) => t.name).join(", ")}`);
596
+ process.exit(1);
597
+ }
598
+ } else {
599
+ templateConfig = await promptTemplateSelection(allTemplates);
600
+ }
601
+ const resolved = resolveTemplate(templatesDir, templateConfig);
602
+ const config = resolved.config;
603
+ const answers = await runPrompts(config.prompts, cliFlags, configVars);
604
+ const vars = computeDerived(config.derived ?? {}, answers, configVars);
605
+ const targetDir = resolveTargetDir(config, vars, configVars);
606
+ if (exists(targetDir)) {
607
+ const overwrite = await p2.confirm({
608
+ message: `Directory ${pc.yellow(targetDir)} already exists. Continue anyway?`,
609
+ initialValue: false
610
+ });
611
+ if (p2.isCancel(overwrite) || !overwrite) {
612
+ p2.cancel("Aborted.");
613
+ process.exit(0);
614
+ }
615
+ }
616
+ const filteredFiles = filterConditionalFiles(
617
+ resolved.files,
618
+ config.conditionalFiles,
619
+ vars,
620
+ configVars
621
+ );
622
+ p2.log.step(`Creating ${pc.cyan(config.displayName)} \u2192 ${pc.dim(targetDir)}`);
623
+ const { copied } = copyFiles(filteredFiles, targetDir, vars, configVars);
624
+ p2.log.success(`Copied ${copied.length} files`);
625
+ const projectConfigPath = path6.join(targetDir, ".rothzerg.json");
626
+ writeJson(projectConfigPath, {
627
+ engine: "rothzerg",
628
+ template: config.name,
629
+ version,
630
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
631
+ variables: vars
632
+ });
633
+ if (config.hooks?.postCreate && config.hooks.postCreate.length > 0) {
634
+ const hookContext = {
635
+ vars,
636
+ configVars,
637
+ projectDir: targetDir,
638
+ builtins: {
639
+ "register-website": registerWebsite
640
+ }
641
+ };
642
+ const results = await runHooks(config.hooks.postCreate, hookContext);
643
+ for (const result of results) {
644
+ if (result.success) {
645
+ p2.log.success(result.hook);
646
+ } else {
647
+ p2.log.warn(`${result.hook} \u2014 ${result.error}`);
648
+ }
649
+ }
650
+ }
651
+ p2.outro(pc.green("Done! Your project is ready."));
652
+ if (config.nextSteps && config.nextSteps.length > 0) {
653
+ console.log("\n Next steps:");
654
+ for (const step of config.nextSteps) {
655
+ console.log(" " + pc.cyan(interpolate(step, vars, configVars)));
656
+ }
657
+ console.log();
658
+ }
659
+ }
660
+ async function promptTemplateSelection(templates) {
661
+ const groups = groupByCategory(templates);
662
+ const options = [];
663
+ for (const [category, items] of groups) {
664
+ const groupLabel = category.charAt(0).toUpperCase() + category.slice(1) + "s";
665
+ for (const tmpl of items) {
666
+ options.push({
667
+ value: tmpl.name,
668
+ label: `${tmpl.icon ?? "\u2022"} ${tmpl.displayName}`,
669
+ hint: tmpl.description,
670
+ group: groupLabel
671
+ });
672
+ }
673
+ }
674
+ const selected = await p2.select({
675
+ message: "What would you like to create?",
676
+ options
677
+ });
678
+ if (p2.isCancel(selected)) {
679
+ p2.cancel("Operation cancelled.");
680
+ process.exit(0);
681
+ }
682
+ return templates.find((t) => t.name === selected);
683
+ }
684
+ function resolveTargetDir(config, vars, configVars) {
685
+ const { target } = config;
686
+ const dirName = interpolate(target.dirName, vars, configVars);
687
+ if (target.type === "subdirectory" && target.baseDir) {
688
+ const baseDir = interpolate(target.baseDir, vars, configVars);
689
+ return path6.resolve(baseDir, dirName);
690
+ }
691
+ return path6.resolve(process.cwd(), dirName);
692
+ }
693
+
694
+ // src/commands/upgrade.ts
695
+ import path7 from "path";
696
+ import fs6 from "fs";
697
+ import * as p3 from "@clack/prompts";
698
+ import pc2 from "picocolors";
699
+ async function upgradeCommand(options) {
700
+ const { projectDir, templatesDir, globalConfig, dryRun, version } = options;
701
+ const configVars = globalConfig;
702
+ const projectConfigPath = path7.join(projectDir, ".rothzerg.json");
703
+ if (!exists(projectConfigPath)) {
704
+ p3.log.error(
705
+ `No ${pc2.yellow(".rothzerg.json")} found in ${projectDir}.
706
+ Run this command from inside a project created with rothzerg.`
707
+ );
708
+ process.exit(1);
709
+ }
710
+ const projectConfig = readJson(projectConfigPath);
711
+ const storedVars = projectConfig.variables;
712
+ p3.intro(`Upgrading ${pc2.cyan(projectConfig.template)} (was v${projectConfig.version} \u2192 now v${version})`);
713
+ const templateConfig = loadTemplateConfig(templatesDir, projectConfig.template);
714
+ const resolved = resolveTemplate(templatesDir, templateConfig);
715
+ const config = resolved.config;
716
+ const vars = computeDerived(config.derived ?? {}, storedVars, configVars);
717
+ const filteredFiles = filterConditionalFiles(
718
+ resolved.files,
719
+ config.conditionalFiles,
720
+ vars,
721
+ configVars
722
+ );
723
+ const upgradeRules = config.upgrade;
724
+ const summary = {
725
+ merged: [],
726
+ overwritten: [],
727
+ added: [],
728
+ skipped: []
729
+ };
730
+ for (const file of filteredFiles) {
731
+ const rel = file.relativePath.replace(/\\/g, "/");
732
+ const destPath = path7.join(projectDir, rel);
733
+ const destExists = exists(destPath);
734
+ if (matchesAnyGlob(upgradeRules.ignore, rel)) {
735
+ summary.skipped.push(rel);
736
+ continue;
737
+ }
738
+ if (matchesAnyGlob(upgradeRules.merge, rel)) {
739
+ if (!dryRun) {
740
+ mergeFile(file.src, destPath, rel);
741
+ }
742
+ summary.merged.push(rel);
743
+ continue;
744
+ }
745
+ if (matchesAnyGlob(upgradeRules.overwrite, rel)) {
746
+ if (!dryRun) {
747
+ if (destExists) {
748
+ fs6.copyFileSync(destPath, destPath + ".rothzerg-backup");
749
+ }
750
+ copyFileWithTransform(file.src, destPath, vars, configVars);
751
+ }
752
+ summary.overwritten.push(rel);
753
+ continue;
754
+ }
755
+ if (!destExists) {
756
+ if (!dryRun) {
757
+ copyFileWithTransform(file.src, destPath, vars, configVars);
758
+ }
759
+ summary.added.push(rel);
760
+ } else {
761
+ summary.skipped.push(rel);
762
+ }
763
+ }
764
+ if (dryRun) {
765
+ p3.log.info(pc2.yellow("Dry run \u2014 no changes were made."));
766
+ console.log();
767
+ }
768
+ if (summary.overwritten.length > 0) {
769
+ p3.log.success(`Overwritten (${summary.overwritten.length}):`);
770
+ for (const f of summary.overwritten) console.log(" " + pc2.cyan(f));
771
+ }
772
+ if (summary.merged.length > 0) {
773
+ p3.log.success(`Merged (${summary.merged.length}):`);
774
+ for (const f of summary.merged) console.log(" " + pc2.cyan(f));
775
+ }
776
+ if (summary.added.length > 0) {
777
+ p3.log.success(`Added (${summary.added.length}):`);
778
+ for (const f of summary.added) console.log(" " + pc2.green(f));
779
+ }
780
+ if (summary.skipped.length > 0) {
781
+ p3.log.info(`Skipped (${summary.skipped.length})`);
782
+ }
783
+ if (!dryRun) {
784
+ writeJson(projectConfigPath, {
785
+ ...projectConfig,
786
+ version
787
+ });
788
+ }
789
+ if (!dryRun && config.hooks?.postUpgrade && config.hooks.postUpgrade.length > 0) {
790
+ const hookContext = {
791
+ vars,
792
+ configVars,
793
+ projectDir,
794
+ builtins: {
795
+ "register-website": registerWebsite
796
+ }
797
+ };
798
+ const results = await runHooks(config.hooks.postUpgrade, hookContext);
799
+ for (const result of results) {
800
+ if (result.success) {
801
+ p3.log.success(result.hook);
802
+ } else {
803
+ p3.log.warn(`${result.hook} \u2014 ${result.error}`);
804
+ }
805
+ }
806
+ }
807
+ p3.outro(pc2.green("Upgrade complete."));
808
+ }
809
+ function mergeFile(srcPath, destPath, rel) {
810
+ if (rel === "package.json" || rel.endsWith("/package.json")) {
811
+ mergePackageJson(srcPath, destPath);
812
+ return;
813
+ }
814
+ if (rel === "pubspec.yaml" || rel.endsWith("/pubspec.yaml")) {
815
+ fs6.copyFileSync(srcPath, destPath);
816
+ return;
817
+ }
818
+ fs6.copyFileSync(srcPath, destPath);
819
+ }
820
+ function mergePackageJson(srcPath, destPath) {
821
+ if (!exists(destPath)) {
822
+ fs6.copyFileSync(srcPath, destPath);
823
+ return;
824
+ }
825
+ const template = JSON.parse(fs6.readFileSync(srcPath, "utf-8"));
826
+ const existing = JSON.parse(fs6.readFileSync(destPath, "utf-8"));
827
+ const merged = {
828
+ ...template,
829
+ // Preserve user fields
830
+ name: existing.name ?? template.name,
831
+ version: existing.version ?? template.version,
832
+ description: existing.description ?? template.description,
833
+ private: existing.private ?? template.private,
834
+ author: existing.author ?? template.author,
835
+ license: existing.license ?? template.license,
836
+ // Merge dependencies: template versions take precedence, user additions kept
837
+ dependencies: mergeDepMap(template.dependencies, existing.dependencies),
838
+ devDependencies: mergeDepMap(template.devDependencies, existing.devDependencies),
839
+ // Scripts: template wins, but user-added scripts are preserved
840
+ scripts: {
841
+ ...existing.scripts ?? {},
842
+ ...template.scripts ?? {}
843
+ }
844
+ };
845
+ fs6.writeFileSync(destPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
846
+ }
847
+ function mergeDepMap(templateDeps, existingDeps) {
848
+ const result = { ...existingDeps ?? {} };
849
+ for (const [pkg2, ver] of Object.entries(templateDeps ?? {})) {
850
+ result[pkg2] = ver;
851
+ }
852
+ return result;
853
+ }
854
+
855
+ // src/cli.ts
856
+ var __filename = fileURLToPath(import.meta.url);
857
+ var __dirname = path8.dirname(__filename);
858
+ var TEMPLATES_DIR = path8.resolve(__dirname, "..", "templates");
859
+ var pkg = JSON.parse(
860
+ fs7.readFileSync(path8.resolve(__dirname, "..", "package.json"), "utf-8")
861
+ );
862
+ var VERSION = pkg.version;
863
+ function loadGlobalConfig() {
864
+ const configPaths = [
865
+ // Project-local config (takes priority)
866
+ path8.resolve(process.cwd(), ".rothzerg", "config.json"),
867
+ // User home config
868
+ path8.resolve(process.env["HOME"] ?? "~", ".rothzerg", "config.json")
869
+ ];
870
+ for (const configPath of configPaths) {
871
+ if (fs7.existsSync(configPath)) {
872
+ try {
873
+ return JSON.parse(fs7.readFileSync(configPath, "utf-8"));
874
+ } catch {
875
+ }
876
+ }
877
+ }
878
+ return {};
879
+ }
880
+ var HELP = `
881
+ ${pc3.bold("rothzerg")} v${VERSION}
882
+
883
+ ${pc3.dim("Plugin-style CLI engine for scaffolding projects from config-driven templates.")}
884
+
885
+ ${pc3.bold("Usage:")}
886
+ ${pc3.cyan("rothzerg create [template]")} Create a new project
887
+ ${pc3.cyan("rothzerg upgrade")} Upgrade the current project
888
+
889
+ ${pc3.bold("Options:")}
890
+ ${pc3.cyan("--help, -h")} Show this help
891
+ ${pc3.cyan("--version, -v")} Show version
892
+ ${pc3.cyan("--dry-run")} (upgrade) Preview without applying changes
893
+
894
+ ${pc3.bold("Examples:")}
895
+ ${pc3.dim("bunx rothzerg create")}
896
+ ${pc3.dim("bunx rothzerg create web-app --name my-app")}
897
+ ${pc3.dim("bunx rothzerg create website-blog --domain myblog.com")}
898
+ ${pc3.dim("bunx rothzerg upgrade")}
899
+ ${pc3.dim("bunx rothzerg upgrade --dry-run")}
900
+ `;
901
+ async function main() {
902
+ const args = process.argv.slice(2);
903
+ if (args.includes("--help") || args.includes("-h") || args.length === 0) {
904
+ console.log(HELP);
905
+ process.exit(0);
906
+ }
907
+ if (args.includes("--version") || args.includes("-v")) {
908
+ console.log(VERSION);
909
+ process.exit(0);
910
+ }
911
+ const command = args[0];
912
+ const rest = args.slice(1);
913
+ p4.intro(`${pc3.bold("rothzerg")} ${pc3.dim(`v${VERSION}`)}`);
914
+ const globalConfig = loadGlobalConfig();
915
+ switch (command) {
916
+ case "create": {
917
+ const templateName = rest[0] && !rest[0].startsWith("--") ? rest[0] : void 0;
918
+ const flagArgs = templateName ? rest.slice(1) : rest;
919
+ const cliFlags = parseCliFlags(flagArgs);
920
+ await createCommand({
921
+ templateName,
922
+ cliFlags,
923
+ templatesDir: TEMPLATES_DIR,
924
+ globalConfig,
925
+ version: VERSION
926
+ });
927
+ break;
928
+ }
929
+ case "upgrade": {
930
+ const dryRun = rest.includes("--dry-run");
931
+ await upgradeCommand({
932
+ dryRun,
933
+ projectDir: process.cwd(),
934
+ templatesDir: TEMPLATES_DIR,
935
+ globalConfig,
936
+ version: VERSION
937
+ });
938
+ break;
939
+ }
940
+ default:
941
+ p4.log.error(`Unknown command: "${command}"`);
942
+ console.log(HELP);
943
+ process.exit(1);
944
+ }
945
+ }
946
+ main().catch((err) => {
947
+ console.error(pc3.red("Fatal error:"), err.message ?? err);
948
+ process.exit(1);
949
+ });