@teardown/cli 1.2.39 → 2.0.41

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 (260) hide show
  1. package/bin/teardown.js +11 -1
  2. package/package.json +77 -57
  3. package/src/cli/commands/init.ts +254 -0
  4. package/src/cli/commands/plugins.ts +93 -0
  5. package/src/cli/commands/prebuild.ts +168 -0
  6. package/src/cli/commands/run.ts +727 -0
  7. package/src/cli/commands/start.ts +87 -0
  8. package/src/cli/commands/validate.ts +62 -0
  9. package/src/cli/index.ts +59 -0
  10. package/src/config/index.ts +45 -0
  11. package/src/config/loader.ts +366 -0
  12. package/src/config/schema.ts +235 -0
  13. package/src/config/types.ts +322 -0
  14. package/src/index.ts +177 -0
  15. package/src/pipeline/cache.ts +179 -0
  16. package/src/pipeline/index.ts +10 -0
  17. package/src/pipeline/stages.ts +692 -0
  18. package/src/plugins/base.ts +370 -0
  19. package/src/plugins/capabilities/biometrics.ts +64 -0
  20. package/src/plugins/capabilities/bluetooth.ts +86 -0
  21. package/src/plugins/capabilities/calendar.ts +57 -0
  22. package/src/plugins/capabilities/camera.ts +77 -0
  23. package/src/plugins/capabilities/contacts.ts +57 -0
  24. package/src/plugins/capabilities/deep-linking.ts +124 -0
  25. package/src/plugins/capabilities/firebase.ts +138 -0
  26. package/src/plugins/capabilities/index.ts +96 -0
  27. package/src/plugins/capabilities/location.ts +87 -0
  28. package/src/plugins/capabilities/photo-library.ts +80 -0
  29. package/src/plugins/capabilities/push-notifications.ts +98 -0
  30. package/src/plugins/capabilities/sign-in-with-apple.ts +53 -0
  31. package/src/plugins/context.ts +220 -0
  32. package/src/plugins/index.ts +26 -0
  33. package/src/plugins/resolver.ts +321 -0
  34. package/src/templates/generator.ts +507 -0
  35. package/src/templates/index.ts +9 -0
  36. package/src/templates/paths.ts +25 -0
  37. package/src/transformers/android/gradle.ts +400 -0
  38. package/src/transformers/android/index.ts +19 -0
  39. package/src/transformers/android/manifest.ts +506 -0
  40. package/src/transformers/index.ts +39 -0
  41. package/src/transformers/ios/entitlements.ts +283 -0
  42. package/src/transformers/ios/index.ts +10 -0
  43. package/src/transformers/ios/pbxproj.ts +267 -0
  44. package/src/transformers/ios/plist.ts +198 -0
  45. package/src/utils/fs.ts +429 -0
  46. package/src/utils/index.ts +21 -0
  47. package/src/utils/logger.ts +203 -0
  48. package/templates/.gitignore +63 -0
  49. package/templates/Gemfile +3 -0
  50. package/templates/android/app/build.gradle.kts +97 -0
  51. package/templates/android/app/proguard-rules.pro +10 -0
  52. package/templates/android/app/src/main/AndroidManifest.xml +26 -0
  53. package/templates/android/app/src/main/java/com/appname/MainActivity.kt +22 -0
  54. package/templates/android/app/src/main/java/com/appname/MainApplication.kt +44 -0
  55. package/templates/android/app/src/main/res/values/strings.xml +3 -0
  56. package/templates/android/app/src/main/res/values/styles.xml +7 -0
  57. package/templates/android/build.gradle.kts +44 -0
  58. package/templates/android/gradle.properties +39 -0
  59. package/templates/android/settings.gradle.kts +12 -0
  60. package/templates/babel.config.js +15 -0
  61. package/templates/index.js +7 -0
  62. package/templates/ios/.xcode.env +11 -0
  63. package/templates/ios/AppName/AppDelegate.swift +25 -0
  64. package/templates/ios/AppName/AppName-Bridging-Header.h +4 -0
  65. package/templates/ios/AppName/AppName.entitlements +6 -0
  66. package/templates/ios/AppName/Images.xcassets/AppIcon.appiconset/Contents.json +35 -0
  67. package/templates/ios/AppName/Images.xcassets/Contents.json +6 -0
  68. package/templates/ios/AppName/Info.plist +49 -0
  69. package/templates/ios/AppName/LaunchScreen.storyboard +38 -0
  70. package/templates/ios/AppName.xcodeproj/project.pbxproj +402 -0
  71. package/templates/ios/AppName.xcodeproj/xcshareddata/xcschemes/AppName.xcscheme +78 -0
  72. package/templates/ios/Podfile +35 -0
  73. package/templates/metro.config.js +41 -0
  74. package/templates/package.json +57 -0
  75. package/templates/react-native.config.js +8 -0
  76. package/templates/src/app/index.tsx +34 -0
  77. package/templates/src/assets/fonts/.gitkeep +1 -0
  78. package/templates/src/assets/images/.gitkeep +1 -0
  79. package/templates/src/components/ui/accordion.tsx +114 -0
  80. package/templates/src/components/ui/avatar.tsx +75 -0
  81. package/templates/src/components/ui/button.tsx +93 -0
  82. package/templates/src/components/ui/card.tsx +120 -0
  83. package/templates/src/components/ui/checkbox.tsx +133 -0
  84. package/templates/src/components/ui/chip.tsx +95 -0
  85. package/templates/src/components/ui/dialog.tsx +134 -0
  86. package/templates/src/components/ui/divider.tsx +67 -0
  87. package/templates/src/components/ui/error-view.tsx +82 -0
  88. package/templates/src/components/ui/form-field.tsx +101 -0
  89. package/templates/src/components/ui/index.ts +100 -0
  90. package/templates/src/components/ui/popover.tsx +92 -0
  91. package/templates/src/components/ui/pressable-feedback.tsx +88 -0
  92. package/templates/src/components/ui/radio-group.tsx +153 -0
  93. package/templates/src/components/ui/scroll-shadow.tsx +108 -0
  94. package/templates/src/components/ui/select.tsx +165 -0
  95. package/templates/src/components/ui/skeleton-group.tsx +97 -0
  96. package/templates/src/components/ui/skeleton.tsx +87 -0
  97. package/templates/src/components/ui/spinner.tsx +87 -0
  98. package/templates/src/components/ui/surface.tsx +95 -0
  99. package/templates/src/components/ui/switch.tsx +124 -0
  100. package/templates/src/components/ui/tabs.tsx +154 -0
  101. package/templates/src/components/ui/text-field.tsx +106 -0
  102. package/templates/src/components/ui/toast.tsx +129 -0
  103. package/templates/src/contexts/.gitkeep +2 -0
  104. package/templates/src/core/clients/api/api.client.ts +113 -0
  105. package/templates/src/core/clients/api/index.ts +1 -0
  106. package/templates/src/core/clients/storage/index.ts +1 -0
  107. package/templates/src/core/clients/storage/storage.client.ts +121 -0
  108. package/templates/src/core/constants/index.ts +19 -0
  109. package/templates/src/core/core.ts +40 -0
  110. package/templates/src/core/index.ts +10 -0
  111. package/templates/src/global.css +87 -0
  112. package/templates/src/hooks/index.ts +6 -0
  113. package/templates/src/hooks/use-debounce.ts +23 -0
  114. package/templates/src/hooks/use-mounted.ts +21 -0
  115. package/templates/src/index.ts +28 -0
  116. package/templates/src/lib/index.ts +5 -0
  117. package/templates/src/lib/utils.ts +115 -0
  118. package/templates/src/modules/.gitkeep +6 -0
  119. package/templates/src/navigation/index.ts +8 -0
  120. package/templates/src/navigation/navigation-provider.tsx +36 -0
  121. package/templates/src/navigation/router.tsx +137 -0
  122. package/templates/src/providers/app.provider.tsx +29 -0
  123. package/templates/src/providers/index.ts +5 -0
  124. package/templates/src/routes/(tabs)/_layout.tsx +42 -0
  125. package/templates/src/routes/(tabs)/explore.tsx +161 -0
  126. package/templates/src/routes/(tabs)/home.tsx +138 -0
  127. package/templates/src/routes/(tabs)/profile.tsx +151 -0
  128. package/templates/src/routes/_layout.tsx +18 -0
  129. package/templates/src/routes/settings.tsx +194 -0
  130. package/templates/src/screens/auth/index.ts +6 -0
  131. package/templates/src/screens/auth/login.tsx +165 -0
  132. package/templates/src/screens/auth/register.tsx +203 -0
  133. package/templates/src/screens/home.tsx +204 -0
  134. package/templates/src/screens/index.ts +17 -0
  135. package/templates/src/screens/profile.tsx +210 -0
  136. package/templates/src/screens/settings.tsx +216 -0
  137. package/templates/src/screens/welcome.tsx +101 -0
  138. package/templates/src/styles/index.ts +103 -0
  139. package/templates/src/types/common.ts +71 -0
  140. package/templates/src/types/index.ts +5 -0
  141. package/templates/tsconfig.json +14 -0
  142. package/README.md +0 -15
  143. package/assets/favicon.ico +0 -0
  144. package/dist/commands/dev/dev.d.ts +0 -22
  145. package/dist/commands/dev/dev.js +0 -56
  146. package/dist/commands/dev/dev.js.map +0 -1
  147. package/dist/commands/init/init-teardown.d.ts +0 -9
  148. package/dist/commands/init/init-teardown.js +0 -27
  149. package/dist/commands/init/init-teardown.js.map +0 -1
  150. package/dist/index.d.ts +0 -1
  151. package/dist/index.js +0 -21
  152. package/dist/index.js.map +0 -1
  153. package/dist/modules/dev/dev-menu/keyboard-handler.d.ts +0 -21
  154. package/dist/modules/dev/dev-menu/keyboard-handler.js +0 -139
  155. package/dist/modules/dev/dev-menu/keyboard-handler.js.map +0 -1
  156. package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.d.ts +0 -18
  157. package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js +0 -106
  158. package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js.map +0 -1
  159. package/dist/modules/dev/dev-server/cdp/cdp.adapter.d.ts +0 -6
  160. package/dist/modules/dev/dev-server/cdp/cdp.adapter.js +0 -13
  161. package/dist/modules/dev/dev-server/cdp/cdp.adapter.js.map +0 -1
  162. package/dist/modules/dev/dev-server/cdp/index.d.ts +0 -2
  163. package/dist/modules/dev/dev-server/cdp/index.js +0 -19
  164. package/dist/modules/dev/dev-server/cdp/index.js.map +0 -1
  165. package/dist/modules/dev/dev-server/cdp/types.d.ts +0 -107
  166. package/dist/modules/dev/dev-server/cdp/types.js +0 -3
  167. package/dist/modules/dev/dev-server/cdp/types.js.map +0 -1
  168. package/dist/modules/dev/dev-server/dev-server-checker.d.ts +0 -22
  169. package/dist/modules/dev/dev-server/dev-server-checker.js +0 -73
  170. package/dist/modules/dev/dev-server/dev-server-checker.js.map +0 -1
  171. package/dist/modules/dev/dev-server/dev-server.d.ts +0 -74
  172. package/dist/modules/dev/dev-server/dev-server.js +0 -272
  173. package/dist/modules/dev/dev-server/dev-server.js.map +0 -1
  174. package/dist/modules/dev/dev-server/inspector/device.d.ts +0 -46
  175. package/dist/modules/dev/dev-server/inspector/device.event-reporter.d.ts +0 -37
  176. package/dist/modules/dev/dev-server/inspector/device.event-reporter.js +0 -166
  177. package/dist/modules/dev/dev-server/inspector/device.event-reporter.js.map +0 -1
  178. package/dist/modules/dev/dev-server/inspector/device.js +0 -578
  179. package/dist/modules/dev/dev-server/inspector/device.js.map +0 -1
  180. package/dist/modules/dev/dev-server/inspector/inspector.d.ts +0 -27
  181. package/dist/modules/dev/dev-server/inspector/inspector.js +0 -225
  182. package/dist/modules/dev/dev-server/inspector/inspector.js.map +0 -1
  183. package/dist/modules/dev/dev-server/inspector/types.d.ts +0 -156
  184. package/dist/modules/dev/dev-server/inspector/types.js +0 -3
  185. package/dist/modules/dev/dev-server/inspector/types.js.map +0 -1
  186. package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.d.ts +0 -14
  187. package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js +0 -63
  188. package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js.map +0 -1
  189. package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.d.ts +0 -19
  190. package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js +0 -66
  191. package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js.map +0 -1
  192. package/dist/modules/dev/dev-server/plugins/devtools.plugin.d.ts +0 -1
  193. package/dist/modules/dev/dev-server/plugins/devtools.plugin.js +0 -51
  194. package/dist/modules/dev/dev-server/plugins/devtools.plugin.js.map +0 -1
  195. package/dist/modules/dev/dev-server/plugins/favicon.plugin.d.ts +0 -1
  196. package/dist/modules/dev/dev-server/plugins/favicon.plugin.js +0 -19
  197. package/dist/modules/dev/dev-server/plugins/favicon.plugin.js.map +0 -1
  198. package/dist/modules/dev/dev-server/plugins/multipart.plugin.d.ts +0 -1
  199. package/dist/modules/dev/dev-server/plugins/multipart.plugin.js +0 -63
  200. package/dist/modules/dev/dev-server/plugins/multipart.plugin.js.map +0 -1
  201. package/dist/modules/dev/dev-server/plugins/systrace.plugin.d.ts +0 -1
  202. package/dist/modules/dev/dev-server/plugins/systrace.plugin.js +0 -29
  203. package/dist/modules/dev/dev-server/plugins/systrace.plugin.js.map +0 -1
  204. package/dist/modules/dev/dev-server/plugins/types.d.ts +0 -11
  205. package/dist/modules/dev/dev-server/plugins/types.js +0 -3
  206. package/dist/modules/dev/dev-server/plugins/types.js.map +0 -1
  207. package/dist/modules/dev/dev-server/plugins/wss/index.d.ts +0 -3
  208. package/dist/modules/dev/dev-server/plugins/wss/index.js +0 -20
  209. package/dist/modules/dev/dev-server/plugins/wss/index.js.map +0 -1
  210. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.d.ts +0 -37
  211. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js +0 -67
  212. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js.map +0 -1
  213. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.d.ts +0 -63
  214. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js +0 -129
  215. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js.map +0 -1
  216. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.d.ts +0 -32
  217. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js +0 -76
  218. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js.map +0 -1
  219. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.d.ts +0 -75
  220. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js +0 -199
  221. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js.map +0 -1
  222. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.d.ts +0 -44
  223. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js +0 -121
  224. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js.map +0 -1
  225. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.d.ts +0 -135
  226. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js +0 -364
  227. package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js.map +0 -1
  228. package/dist/modules/dev/dev-server/plugins/wss/types.d.ts +0 -6
  229. package/dist/modules/dev/dev-server/plugins/wss/types.js +0 -3
  230. package/dist/modules/dev/dev-server/plugins/wss/types.js.map +0 -1
  231. package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.d.ts +0 -32
  232. package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js +0 -58
  233. package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js.map +0 -1
  234. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.d.ts +0 -13
  235. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js +0 -27
  236. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js.map +0 -1
  237. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.d.ts +0 -39
  238. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js +0 -47
  239. package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js.map +0 -1
  240. package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.d.ts +0 -24
  241. package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js +0 -56
  242. package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js.map +0 -1
  243. package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.d.ts +0 -7
  244. package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js +0 -41
  245. package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js.map +0 -1
  246. package/dist/modules/dev/dev-server/sybmolicate/types.d.ts +0 -64
  247. package/dist/modules/dev/dev-server/sybmolicate/types.js +0 -3
  248. package/dist/modules/dev/dev-server/sybmolicate/types.js.map +0 -1
  249. package/dist/modules/dev/terminal/base.terminal.reporter.d.ts +0 -25
  250. package/dist/modules/dev/terminal/base.terminal.reporter.js +0 -79
  251. package/dist/modules/dev/terminal/base.terminal.reporter.js.map +0 -1
  252. package/dist/modules/dev/terminal/terminal.reporter.d.ts +0 -13
  253. package/dist/modules/dev/terminal/terminal.reporter.js +0 -83
  254. package/dist/modules/dev/terminal/terminal.reporter.js.map +0 -1
  255. package/dist/modules/dev/types.d.ts +0 -20
  256. package/dist/modules/dev/types.js +0 -3
  257. package/dist/modules/dev/types.js.map +0 -1
  258. package/dist/modules/dev/utils/log.d.ts +0 -23
  259. package/dist/modules/dev/utils/log.js +0 -74
  260. package/dist/modules/dev/utils/log.js.map +0 -1
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Template Generator
3
+ *
4
+ * Handles copying and processing template files for iOS and Android projects.
5
+ * Uses EJS for template processing and handles dynamic file/folder renaming.
6
+ */
7
+
8
+ import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
9
+ import { dirname, join } from "node:path";
10
+ import ejs from "ejs";
11
+ import type { VirtualFileSystem } from "../utils/fs";
12
+ import type { Logger } from "../utils/logger";
13
+ import { getTemplatePath, getTemplatesPath } from "./paths";
14
+
15
+ /**
16
+ * Template variables for EJS processing
17
+ */
18
+ export interface TemplateVariables {
19
+ // Common variables
20
+ slug: string;
21
+
22
+ // iOS variables
23
+ appName: string;
24
+ bundleIdentifier: string;
25
+ version: string;
26
+ buildNumber: number;
27
+
28
+ // Android variables
29
+ packageName: string;
30
+ versionCode: number;
31
+ versionName: string;
32
+
33
+ // CLI version (for package.json template)
34
+ cliVersion?: string;
35
+ }
36
+
37
+ /**
38
+ * Template generation options
39
+ */
40
+ export interface TemplateOptions {
41
+ projectRoot: string;
42
+ variables: TemplateVariables;
43
+ logger?: Logger;
44
+ fs?: VirtualFileSystem;
45
+ dryRun?: boolean;
46
+ force?: boolean; // Overwrite existing files
47
+ }
48
+
49
+ /**
50
+ * Result of template generation
51
+ */
52
+ export interface TemplateResult {
53
+ success: boolean;
54
+ filesCreated: string[];
55
+ filesSkipped: string[];
56
+ errors: string[];
57
+ }
58
+
59
+ /**
60
+ * Template Generator class
61
+ */
62
+ export class TemplateGenerator {
63
+ private options: TemplateOptions;
64
+ private logger?: Logger;
65
+
66
+ constructor(options: TemplateOptions) {
67
+ this.options = options;
68
+ this.logger = options.logger;
69
+ }
70
+
71
+ /**
72
+ * Generate iOS project from templates
73
+ */
74
+ async generateiOS(): Promise<TemplateResult> {
75
+ const templatePath = getTemplatePath("ios");
76
+ const outputPath = join(this.options.projectRoot, "ios");
77
+
78
+ this.logger?.info("Generating iOS project from template...");
79
+
80
+ return this.processTemplateDirectory(templatePath, outputPath, "ios");
81
+ }
82
+
83
+ /**
84
+ * Generate Android project from templates
85
+ */
86
+ async generateAndroid(): Promise<TemplateResult> {
87
+ const templatePath = getTemplatePath("android");
88
+ const outputPath = join(this.options.projectRoot, "android");
89
+
90
+ this.logger?.info("Generating Android project from template...");
91
+
92
+ return this.processTemplateDirectory(templatePath, outputPath, "android");
93
+ }
94
+
95
+ /**
96
+ * Generate both iOS and Android projects
97
+ */
98
+ async generateAll(): Promise<{ ios: TemplateResult; android: TemplateResult }> {
99
+ const ios = await this.generateiOS();
100
+ const android = await this.generateAndroid();
101
+ return { ios, android };
102
+ }
103
+
104
+ /**
105
+ * Check if iOS project already exists
106
+ */
107
+ async iosProjectExists(): Promise<boolean> {
108
+ const xcodeProjectPath = join(this.options.projectRoot, "ios", `${this.options.variables.appName}.xcodeproj`);
109
+ try {
110
+ await stat(xcodeProjectPath);
111
+ return true;
112
+ } catch {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Check if Android project already exists
119
+ */
120
+ async androidProjectExists(): Promise<boolean> {
121
+ const manifestPath = join(this.options.projectRoot, "android", "app", "src", "main", "AndroidManifest.xml");
122
+ try {
123
+ await stat(manifestPath);
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Check if package.json exists
132
+ */
133
+ async packageJsonExists(): Promise<boolean> {
134
+ const packagePath = join(this.options.projectRoot, "package.json");
135
+ try {
136
+ await stat(packagePath);
137
+ return true;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Check if src folder exists
145
+ */
146
+ async srcFolderExists(): Promise<boolean> {
147
+ const srcPath = join(this.options.projectRoot, "src", "index.ts");
148
+ try {
149
+ await stat(srcPath);
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Generate src folder from template
158
+ */
159
+ async generateSrcFolder(): Promise<TemplateResult> {
160
+ const templatePath = join(getTemplatesPath(), "src");
161
+ const outputPath = join(this.options.projectRoot, "src");
162
+
163
+ this.logger?.info("Generating src folder from template...");
164
+
165
+ return this.processTemplateDirectory(templatePath, outputPath, "ios"); // Use ios as platform for EJS processing
166
+ }
167
+
168
+ /**
169
+ * Generate root config files (tsconfig.json, babel.config.js, metro.config.js, index.js)
170
+ */
171
+ async generateRootConfigFiles(): Promise<TemplateResult> {
172
+ const result: TemplateResult = {
173
+ success: true,
174
+ filesCreated: [],
175
+ filesSkipped: [],
176
+ errors: [],
177
+ };
178
+
179
+ const configFiles = [
180
+ "tsconfig.json",
181
+ "babel.config.js",
182
+ "metro.config.js",
183
+ "index.js",
184
+ "react-native.config.js",
185
+ "Gemfile",
186
+ ];
187
+
188
+ for (const file of configFiles) {
189
+ const templatePath = join(getTemplatesPath(), file);
190
+ const outputPath = join(this.options.projectRoot, file);
191
+
192
+ try {
193
+ // Check if template file exists
194
+ try {
195
+ await stat(templatePath);
196
+ } catch {
197
+ // Template doesn't exist, skip
198
+ continue;
199
+ }
200
+
201
+ // Check if output file already exists
202
+ if (!this.options.force) {
203
+ try {
204
+ await stat(outputPath);
205
+ result.filesSkipped.push(outputPath);
206
+ this.logger?.debug(`${file} already exists, skipping`);
207
+ continue;
208
+ } catch {
209
+ // File doesn't exist, continue
210
+ }
211
+ }
212
+
213
+ const content = await readFile(templatePath, "utf-8");
214
+ let processedContent = content;
215
+
216
+ // Process EJS if the file contains EJS tags
217
+ if (content.includes("<%=") || content.includes("<%")) {
218
+ processedContent = ejs.render(content, this.options.variables, {
219
+ filename: templatePath,
220
+ });
221
+ }
222
+
223
+ if (!this.options.dryRun) {
224
+ await writeFile(outputPath, processedContent, "utf-8");
225
+ }
226
+
227
+ result.filesCreated.push(outputPath);
228
+ this.logger?.debug(`Created: ${outputPath}`);
229
+ } catch (error) {
230
+ result.errors.push(`Failed to generate ${file}: ${error instanceof Error ? error.message : String(error)}`);
231
+ }
232
+ }
233
+
234
+ if (result.errors.length > 0) {
235
+ result.success = false;
236
+ }
237
+
238
+ return result;
239
+ }
240
+
241
+ /**
242
+ * Generate .gitignore from template
243
+ */
244
+ async generateGitignore(): Promise<TemplateResult> {
245
+ const result: TemplateResult = {
246
+ success: true,
247
+ filesCreated: [],
248
+ filesSkipped: [],
249
+ errors: [],
250
+ };
251
+
252
+ const templatePath = join(getTemplatesPath(), ".gitignore");
253
+ const outputPath = join(this.options.projectRoot, ".gitignore");
254
+
255
+ try {
256
+ // Check if .gitignore already exists
257
+ if (!this.options.force) {
258
+ try {
259
+ await stat(outputPath);
260
+ result.filesSkipped.push(outputPath);
261
+ this.logger?.debug(".gitignore already exists, skipping");
262
+ return result;
263
+ } catch {
264
+ // File doesn't exist, continue
265
+ }
266
+ }
267
+
268
+ const content = await readFile(templatePath, "utf-8");
269
+
270
+ if (!this.options.dryRun) {
271
+ await writeFile(outputPath, content, "utf-8");
272
+ }
273
+
274
+ result.filesCreated.push(outputPath);
275
+ this.logger?.debug(`Created: ${outputPath}`);
276
+ } catch (error) {
277
+ result.success = false;
278
+ result.errors.push(`Failed to generate .gitignore: ${error instanceof Error ? error.message : String(error)}`);
279
+ }
280
+
281
+ return result;
282
+ }
283
+
284
+ /**
285
+ * Generate package.json from template
286
+ */
287
+ async generatePackageJson(): Promise<TemplateResult> {
288
+ const result: TemplateResult = {
289
+ success: true,
290
+ filesCreated: [],
291
+ filesSkipped: [],
292
+ errors: [],
293
+ };
294
+
295
+ const templatePath = join(getTemplatesPath(), "package.json");
296
+ const outputPath = join(this.options.projectRoot, "package.json");
297
+
298
+ try {
299
+ // Check if package.json already exists
300
+ if (!this.options.force) {
301
+ try {
302
+ await stat(outputPath);
303
+ result.filesSkipped.push(outputPath);
304
+ this.logger?.debug("package.json already exists, skipping");
305
+ return result;
306
+ } catch {
307
+ // File doesn't exist, continue
308
+ }
309
+ }
310
+
311
+ const content = await readFile(templatePath, "utf-8");
312
+ const processedContent = ejs.render(content, this.options.variables, {
313
+ filename: templatePath,
314
+ });
315
+
316
+ if (!this.options.dryRun) {
317
+ await writeFile(outputPath, processedContent, "utf-8");
318
+ }
319
+
320
+ result.filesCreated.push(outputPath);
321
+ this.logger?.debug(`Created: ${outputPath}`);
322
+ } catch (error) {
323
+ result.success = false;
324
+ result.errors.push(`Failed to generate package.json: ${error instanceof Error ? error.message : String(error)}`);
325
+ }
326
+
327
+ return result;
328
+ }
329
+
330
+ /**
331
+ * Process a template directory recursively
332
+ */
333
+ private async processTemplateDirectory(
334
+ templateDir: string,
335
+ outputDir: string,
336
+ platform: "ios" | "android"
337
+ ): Promise<TemplateResult> {
338
+ const result: TemplateResult = {
339
+ success: true,
340
+ filesCreated: [],
341
+ filesSkipped: [],
342
+ errors: [],
343
+ };
344
+
345
+ try {
346
+ await this.processDirectory(templateDir, outputDir, platform, result);
347
+ } catch (error) {
348
+ result.success = false;
349
+ result.errors.push(error instanceof Error ? error.message : String(error));
350
+ }
351
+
352
+ return result;
353
+ }
354
+
355
+ /**
356
+ * Recursively process a directory
357
+ */
358
+ private async processDirectory(
359
+ srcDir: string,
360
+ destDir: string,
361
+ platform: "ios" | "android",
362
+ result: TemplateResult
363
+ ): Promise<void> {
364
+ const entries = await readdir(srcDir, { withFileTypes: true });
365
+
366
+ // Create destination directory
367
+ if (!this.options.dryRun) {
368
+ await mkdir(destDir, { recursive: true });
369
+ }
370
+
371
+ for (const entry of entries) {
372
+ const srcPath = join(srcDir, entry.name);
373
+
374
+ // Special handling for Android java source directories
375
+ // When we encounter "com/appname", we need to replace it with the actual package path
376
+ if (platform === "android" && entry.name === "com" && entry.isDirectory()) {
377
+ // Check if this is the com/appname directory in the template
378
+ const appnamePath = join(srcPath, "appname");
379
+ try {
380
+ const appnameStat = await stat(appnamePath);
381
+ if (appnameStat.isDirectory()) {
382
+ // Replace com/appname with the actual package path
383
+ const packagePath = this.options.variables.packageName.replace(/\./g, "/");
384
+ const newDestDir = join(destDir, packagePath);
385
+
386
+ // Process files in com/appname and put them in the correct package directory
387
+ await this.processDirectory(appnamePath, newDestDir, platform, result);
388
+ continue;
389
+ }
390
+ } catch {
391
+ // com/appname doesn't exist, process normally
392
+ }
393
+ }
394
+
395
+ const destName = this.replaceTemplateNames(entry.name, platform);
396
+ const destPath = join(destDir, destName);
397
+
398
+ if (entry.isDirectory()) {
399
+ await this.processDirectory(srcPath, destPath, platform, result);
400
+ } else {
401
+ await this.processFile(srcPath, destPath, platform, result);
402
+ }
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Process a single template file
408
+ */
409
+ private async processFile(
410
+ srcPath: string,
411
+ destPath: string,
412
+ platform: "ios" | "android",
413
+ result: TemplateResult
414
+ ): Promise<void> {
415
+ // Check if destination exists
416
+ if (!this.options.force) {
417
+ try {
418
+ await stat(destPath);
419
+ result.filesSkipped.push(destPath);
420
+ this.logger?.debug(`Skipping existing file: ${destPath}`);
421
+ return;
422
+ } catch {
423
+ // File doesn't exist, continue
424
+ }
425
+ }
426
+
427
+ try {
428
+ const content = await readFile(srcPath, "utf-8");
429
+ let processedContent: string;
430
+
431
+ // Check if file needs EJS processing (contains <%= or <% tags)
432
+ if (this.shouldProcessAsTemplate(content)) {
433
+ processedContent = ejs.render(content, this.options.variables, {
434
+ filename: srcPath,
435
+ });
436
+ } else {
437
+ processedContent = content;
438
+ }
439
+
440
+ // Replace template names in content as well (for things like package names in paths)
441
+ processedContent = this.replaceTemplateNamesInContent(processedContent, platform);
442
+
443
+ if (!this.options.dryRun) {
444
+ // Ensure directory exists
445
+ await mkdir(dirname(destPath), { recursive: true });
446
+
447
+ // Write file
448
+ await writeFile(destPath, processedContent, "utf-8");
449
+ }
450
+
451
+ result.filesCreated.push(destPath);
452
+ this.logger?.debug(`Created: ${destPath}`);
453
+ } catch (error) {
454
+ const errorMessage = `Failed to process ${srcPath}: ${error instanceof Error ? error.message : String(error)}`;
455
+ result.errors.push(errorMessage);
456
+ this.logger?.error(errorMessage);
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Check if a file should be processed as an EJS template
462
+ */
463
+ private shouldProcessAsTemplate(content: string): boolean {
464
+ return content.includes("<%=") || content.includes("<%");
465
+ }
466
+
467
+ /**
468
+ * Replace template placeholder names in file/folder names
469
+ */
470
+ private replaceTemplateNames(name: string, platform: "ios" | "android"): string {
471
+ const { appName, packageName } = this.options.variables;
472
+
473
+ let result = name;
474
+
475
+ // iOS replacements
476
+ if (platform === "ios") {
477
+ result = result.replace(/AppName/g, appName);
478
+ }
479
+
480
+ // Android replacements
481
+ if (platform === "android") {
482
+ // Replace com/appname path structure with actual package path
483
+ if (name === "appname" || result.includes("appname")) {
484
+ const packagePath = packageName.replace(/\./g, "/");
485
+ result = result.replace(/com\/appname/g, packagePath);
486
+ result = result.replace(/appname/g, packagePath.split("/").pop() || appName.toLowerCase());
487
+ }
488
+ }
489
+
490
+ return result;
491
+ }
492
+
493
+ /**
494
+ * Replace template names in file content
495
+ */
496
+ private replaceTemplateNamesInContent(content: string, _platform: "ios" | "android"): string {
497
+ // These are already handled by EJS, but this catches any remaining placeholders
498
+ return content;
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Create a template generator instance
504
+ */
505
+ export function createTemplateGenerator(options: TemplateOptions): TemplateGenerator {
506
+ return new TemplateGenerator(options);
507
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Template generation system for React Native projects
3
+ *
4
+ * Similar to Expo's approach, this module handles copying base templates
5
+ * and processing EJS variables to generate native iOS and Android projects.
6
+ */
7
+
8
+ export { TemplateGenerator, type TemplateOptions, type TemplateVariables } from "./generator";
9
+ export { getTemplatePath, getTemplatesPath } from "./paths";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Template path utilities
3
+ */
4
+
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ /**
9
+ * Get the absolute path to the templates directory
10
+ */
11
+ export function getTemplatesPath(): string {
12
+ // Get the directory of this file
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ // Templates are in ../../templates relative to this file (src/templates/paths.ts)
17
+ return join(__dirname, "..", "..", "templates");
18
+ }
19
+
20
+ /**
21
+ * Get the path to a specific template
22
+ */
23
+ export function getTemplatePath(platform: "ios" | "android"): string {
24
+ return join(getTemplatesPath(), platform);
25
+ }