alchemy 0.2.5 → 0.3.1

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 (197) hide show
  1. package/README.md +3 -3
  2. package/lib/ai/approve.d.ts +99 -0
  3. package/lib/ai/approve.js +76 -0
  4. package/lib/ai/astro-file.d.ts +137 -0
  5. package/lib/ai/astro-file.js +156 -0
  6. package/lib/ai/client.d.ts +7 -8
  7. package/lib/ai/client.js +48 -19
  8. package/lib/ai/css-file.d.ts +131 -0
  9. package/lib/ai/css-file.js +141 -0
  10. package/lib/ai/data.d.ts +51 -6
  11. package/lib/ai/data.js +58 -12
  12. package/lib/ai/document.d.ts +108 -24
  13. package/lib/ai/document.js +166 -52
  14. package/lib/ai/html-file.d.ts +128 -0
  15. package/lib/ai/html-file.js +138 -0
  16. package/lib/ai/index.d.ts +9 -0
  17. package/lib/ai/index.js +9 -0
  18. package/lib/ai/json-file.d.ts +160 -0
  19. package/lib/ai/json-file.js +187 -0
  20. package/lib/ai/review.d.ts +122 -0
  21. package/lib/ai/review.js +101 -0
  22. package/lib/ai/typescript-file.d.ts +139 -0
  23. package/lib/ai/typescript-file.js +145 -0
  24. package/lib/ai/vue-file.d.ts +126 -0
  25. package/lib/ai/vue-file.js +136 -0
  26. package/lib/ai/yaml-file.d.ts +169 -0
  27. package/lib/ai/yaml-file.js +200 -0
  28. package/lib/alchemy.js +17 -2
  29. package/lib/apply.js +1 -1
  30. package/lib/cloudflare/dns.d.ts +88 -0
  31. package/lib/cloudflare/dns.js +192 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +1 -1
  33. package/lib/cloudflare/index.d.ts +2 -1
  34. package/lib/cloudflare/index.js +2 -1
  35. package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
  36. package/lib/cloudflare/r2-rest-state-store.js +254 -0
  37. package/lib/cloudflare/response.d.ts +12 -0
  38. package/lib/cloudflare/response.js +0 -0
  39. package/lib/cloudflare/static-site-router.js +16 -7
  40. package/lib/cloudflare/static-site.js +3 -0
  41. package/lib/cloudflare/upload-asset-manifest.js +4 -2
  42. package/lib/cloudflare/zone.d.ts +18 -0
  43. package/lib/cloudflare/zone.js +17 -4
  44. package/lib/dns/godaddy.d.ts +7 -0
  45. package/lib/dns/godaddy.js +21 -0
  46. package/lib/dns/import-dns.d.ts +82 -0
  47. package/lib/dns/import-dns.js +114 -0
  48. package/lib/dns/index.d.ts +2 -0
  49. package/lib/dns/index.js +2 -0
  50. package/lib/dns/record.d.ts +88 -0
  51. package/lib/dns/record.js +13 -0
  52. package/lib/fs/copy-file.d.ts +54 -0
  53. package/lib/fs/copy-file.js +63 -0
  54. package/lib/fs/file-system-state-store.d.ts +17 -0
  55. package/lib/fs/file-system-state-store.js +87 -0
  56. package/lib/fs/file.d.ts +13 -0
  57. package/lib/fs/file.js +24 -5
  58. package/lib/fs/folder.d.ts +10 -0
  59. package/lib/fs/folder.js +2 -2
  60. package/lib/fs/index.d.ts +10 -4
  61. package/lib/fs/index.js +10 -4
  62. package/lib/fs/static-astro-file.d.ts +34 -0
  63. package/lib/fs/static-astro-file.js +39 -0
  64. package/lib/fs/static-css-file.d.ts +24 -0
  65. package/lib/fs/static-css-file.js +29 -0
  66. package/lib/fs/static-html-file.d.ts +31 -0
  67. package/lib/fs/static-html-file.js +36 -0
  68. package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +3 -3
  69. package/lib/fs/static-json-file.js +15 -0
  70. package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
  71. package/lib/fs/static-text-file.js +8 -0
  72. package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +3 -3
  73. package/lib/fs/{typescript-file.js → static-typescript-file.js} +3 -2
  74. package/lib/fs/static-vue-file.d.ts +28 -0
  75. package/lib/fs/static-vue-file.js +33 -0
  76. package/lib/fs/static-yaml-file.d.ts +22 -0
  77. package/lib/fs/{yaml-file.js → static-yaml-file.js} +3 -2
  78. package/lib/internal/getting-started.d.ts +21 -0
  79. package/lib/internal/getting-started.js +87 -0
  80. package/lib/internal/index.d.ts +3 -0
  81. package/lib/internal/index.js +3 -0
  82. package/lib/internal/providers.d.ts +27 -0
  83. package/lib/internal/providers.js +172 -0
  84. package/lib/internal/tutorial.d.ts +104 -0
  85. package/lib/internal/tutorial.js +251 -0
  86. package/lib/resource.js +8 -1
  87. package/lib/scope.d.ts +1 -1
  88. package/lib/scope.js +4 -2
  89. package/lib/state.d.ts +1 -16
  90. package/lib/state.js +0 -87
  91. package/lib/test/bun.d.ts +11 -0
  92. package/lib/test/bun.js +36 -8
  93. package/lib/web/astro.d.ts +147 -0
  94. package/lib/web/astro.js +414 -0
  95. package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
  96. package/lib/web/shadcn.d.ts +97 -0
  97. package/lib/web/shadcn.js +99 -0
  98. package/lib/web/tailwind.d.ts +64 -0
  99. package/lib/web/tailwind.js +97 -0
  100. package/lib/{vite → web}/vite.d.ts +5 -0
  101. package/lib/{vite → web}/vite.js +44 -44
  102. package/lib/web/vitepress/config.d.ts +10 -0
  103. package/lib/web/vitepress/config.js +31 -0
  104. package/lib/web/vitepress/custom-theme.d.ts +173 -0
  105. package/lib/web/vitepress/custom-theme.js +336 -0
  106. package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
  107. package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
  108. package/lib/web/vitepress/home-page.d.ts +246 -0
  109. package/lib/web/vitepress/home-page.js +147 -0
  110. package/lib/{vitepress → web/vitepress}/index.d.ts +2 -0
  111. package/lib/{vitepress → web/vitepress}/index.js +2 -0
  112. package/lib/{vitepress → web/vitepress}/vitepress.d.ts +15 -12
  113. package/lib/{vitepress → web/vitepress}/vitepress.js +26 -44
  114. package/package.json +6 -2
  115. package/src/ai/approve.ts +163 -0
  116. package/src/ai/astro-file.ts +269 -0
  117. package/src/ai/client.ts +60 -21
  118. package/src/ai/css-file.ts +247 -0
  119. package/src/ai/data.ts +81 -19
  120. package/src/ai/document.ts +240 -62
  121. package/src/ai/html-file.ts +244 -0
  122. package/src/ai/index.ts +9 -0
  123. package/src/ai/json-file.ts +325 -0
  124. package/src/ai/review.ts +213 -0
  125. package/src/ai/typescript-file.ts +259 -0
  126. package/src/ai/vue-file.ts +242 -0
  127. package/src/ai/yaml-file.ts +328 -0
  128. package/src/alchemy.ts +18 -7
  129. package/src/apply.ts +8 -9
  130. package/src/cloudflare/dns.ts +344 -0
  131. package/src/cloudflare/generate-asset-manifest.ts +4 -4
  132. package/src/cloudflare/index.ts +2 -1
  133. package/src/cloudflare/r2-rest-state-store.ts +353 -0
  134. package/src/cloudflare/response.ts +9 -0
  135. package/src/cloudflare/static-site-router.ts +18 -8
  136. package/src/cloudflare/static-site.ts +11 -7
  137. package/src/cloudflare/upload-asset-manifest.ts +6 -4
  138. package/src/cloudflare/worker.ts +44 -43
  139. package/src/cloudflare/zone.ts +39 -23
  140. package/src/dns/godaddy.ts +29 -0
  141. package/src/dns/import-dns.ts +213 -0
  142. package/src/dns/index.ts +2 -0
  143. package/src/dns/record.ts +121 -0
  144. package/src/fs/copy-file.ts +115 -0
  145. package/src/fs/file-system-state-store.ts +109 -0
  146. package/src/fs/file.ts +35 -9
  147. package/src/fs/folder.ts +14 -2
  148. package/src/fs/index.ts +10 -4
  149. package/src/fs/static-astro-file.ts +45 -0
  150. package/src/fs/static-css-file.ts +35 -0
  151. package/src/fs/static-html-file.ts +42 -0
  152. package/src/fs/static-json-file.ts +34 -0
  153. package/src/fs/{text-file.ts → static-text-file.ts} +9 -3
  154. package/src/fs/{typescript-file.ts → static-typescript-file.ts} +7 -6
  155. package/src/fs/static-vue-file.ts +39 -0
  156. package/src/fs/static-yaml-file.ts +33 -0
  157. package/src/internal/getting-started.ts +115 -0
  158. package/src/internal/index.ts +3 -0
  159. package/src/internal/providers.ts +241 -0
  160. package/src/internal/tutorial.ts +392 -0
  161. package/src/resource.ts +10 -1
  162. package/src/scope.ts +5 -6
  163. package/src/state.ts +1 -111
  164. package/src/test/bun.ts +60 -10
  165. package/src/web/astro.ts +644 -0
  166. package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
  167. package/src/web/shadcn.ts +219 -0
  168. package/src/web/tailwind.ts +167 -0
  169. package/src/{vite → web}/vite.ts +54 -56
  170. package/src/web/vitepress/config.ts +48 -0
  171. package/src/web/vitepress/custom-theme.ts +514 -0
  172. package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
  173. package/src/web/vitepress/home-page.ts +375 -0
  174. package/src/{vitepress → web/vitepress}/index.ts +2 -0
  175. package/src/{vitepress → web/vitepress}/vitepress.ts +62 -72
  176. package/lib/cloudflare/state.d.ts +0 -82
  177. package/lib/cloudflare/state.js +0 -108
  178. package/lib/fs/json-file.js +0 -7
  179. package/lib/fs/text-file.js +0 -7
  180. package/lib/fs/yaml-file.d.ts +0 -19
  181. package/lib/internal/docs.d.ts +0 -5
  182. package/lib/internal/docs.js +0 -287
  183. package/lib/shadcn/index.d.ts +0 -1
  184. package/lib/shadcn/index.js +0 -1
  185. package/lib/vite/index.d.ts +0 -1
  186. package/lib/vite/index.js +0 -1
  187. package/lib/vitepress/home-page.d.ts +0 -133
  188. package/lib/vitepress/home-page.js +0 -11
  189. package/src/cloudflare/state.ts +0 -138
  190. package/src/fs/json-file.ts +0 -23
  191. package/src/fs/yaml-file.ts +0 -26
  192. package/src/internal/docs.ts +0 -338
  193. package/src/shadcn/index.ts +0 -1
  194. package/src/vite/index.ts +0 -1
  195. package/src/vitepress/home-page.ts +0 -166
  196. /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
  197. /package/src/{vitepress → web/vitepress}/index.md +0 -0
@@ -0,0 +1,219 @@
1
+ import { exec } from "child_process";
2
+ import path from "path";
3
+ import { promisify } from "util";
4
+ import type { Context } from "../context";
5
+ import { Folder, StaticJsonFile } from "../fs";
6
+ import { StaticTypeScriptFile } from "../fs/static-typescript-file";
7
+ import { Resource } from "../resource";
8
+ import { ShadcnComponent } from "./shadcn-component";
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ /**
13
+ * Properties for initializing Shadcn UI
14
+ */
15
+ export interface ShadcnUIProps {
16
+ /**
17
+ * The working directory where Shadcn UI should be installed
18
+ */
19
+ cwd: string;
20
+
21
+ /**
22
+ * The base color to use
23
+ * @default "neutral"
24
+ */
25
+ baseColor?: "neutral" | "gray" | "zinc" | "stone" | "slate";
26
+
27
+ /**
28
+ * Use default configuration
29
+ * @default false
30
+ */
31
+ defaults?: boolean;
32
+
33
+ /**
34
+ * Force overwrite of existing configuration
35
+ * @default false
36
+ */
37
+ force?: boolean;
38
+
39
+ /**
40
+ * Mute output
41
+ * @default false
42
+ */
43
+ silent?: boolean;
44
+
45
+ /**
46
+ * Use the src directory when creating a new project
47
+ * @default true
48
+ */
49
+ srcDir?: boolean;
50
+
51
+ /**
52
+ * Use css variables for theming
53
+ * @default true
54
+ */
55
+ cssVariables?: boolean;
56
+
57
+ /**
58
+ * The components to add
59
+ */
60
+ components?: string[];
61
+
62
+ /**
63
+ * Whether tailwind is already installed and configured.
64
+ * Shadcn UI requires Tailwind CSS to be installed and configured.
65
+ * @default false
66
+ */
67
+ tailwind?: boolean;
68
+
69
+ /**
70
+ * Whether react is already installed
71
+ * @default false
72
+ */
73
+ react?: boolean;
74
+ }
75
+
76
+ /**
77
+ * Shadcn UI resource
78
+ */
79
+ export interface ShadcnUI extends ShadcnUIProps, Resource<"project::ShadcnUI"> {
80
+ /**
81
+ * The working directory where Shadcn UI is installed
82
+ */
83
+ cwd: string;
84
+
85
+ /**
86
+ * The ui directory
87
+ */
88
+ ui: Folder;
89
+
90
+ /**
91
+ * The lib directory
92
+ */
93
+ lib: Folder;
94
+ }
95
+
96
+ /**
97
+ * Initializes Shadcn UI in a project directory
98
+ *
99
+ * @example
100
+ * // Initialize Shadcn UI with default settings
101
+ * const shadcn = await ShadcnUI("my-shadcn", {
102
+ * cwd: "my-project",
103
+ * tailwind: true, // Tailwind must be installed separately
104
+ * react: true // React must be installed separately
105
+ * });
106
+ *
107
+ * @example
108
+ * // Initialize Shadcn UI with custom settings and components
109
+ * const shadcn = await ShadcnUI("custom-shadcn", {
110
+ * cwd: "my-project",
111
+ * baseColor: "zinc",
112
+ * force: true,
113
+ * components: ["button", "card", "input"],
114
+ * tailwind: true, // Tailwind must be installed separately
115
+ * react: true // React must be installed separately
116
+ * });
117
+ */
118
+ export const ShadcnUI = Resource(
119
+ "project::ShadcnUI",
120
+ async function (
121
+ this: Context<ShadcnUI>,
122
+ id: string,
123
+ props: ShadcnUIProps,
124
+ ): Promise<ShadcnUI> {
125
+ if (this.phase === "delete") {
126
+ // For a delete phase, we don't perform any action
127
+ // as removing Shadcn UI would require removing many files
128
+ // which could be destructive to the project
129
+ console.log(
130
+ "Note: ShadcnUI delete phase does not remove installed components",
131
+ );
132
+
133
+ return this.destroy();
134
+ }
135
+
136
+ // Setup execAsync with the correct cwd
137
+ const exec = (command: string) => execAsync(command, { cwd: props.cwd });
138
+
139
+ // Ensure React is installed if not already
140
+ if (!props.react) {
141
+ await exec(`bun add react react-dom`);
142
+ }
143
+
144
+ // Install shadcn dependencies
145
+ await exec(`bun add -D @types/node`);
146
+
147
+ // Create components.json directly instead of running shadcn init
148
+ await StaticJsonFile(path.join(props.cwd, "components.json"), {
149
+ $schema: "https://ui.shadcn.com/schema.json",
150
+ style: props.cssVariables !== false ? "default" : "new-york",
151
+ tsx: true,
152
+ rsc: false,
153
+ tailwind: {
154
+ config: "tailwind.config.js",
155
+ css:
156
+ props.srcDir !== false
157
+ ? "src/styles/globals.css"
158
+ : "styles/globals.css",
159
+ baseColor: props.baseColor || "neutral",
160
+ cssVariables: props.cssVariables !== false,
161
+ },
162
+ aliases: {
163
+ components: props.srcDir !== false ? "@/components" : "./components",
164
+ utils: props.srcDir !== false ? "@/lib/utils" : "./lib/utils",
165
+ },
166
+ });
167
+
168
+ // Ensure ui components directory exists
169
+ const ui = await Folder(
170
+ path.join(
171
+ props.cwd,
172
+ props.srcDir !== false ? "src" : "",
173
+ "components",
174
+ "ui",
175
+ ),
176
+ );
177
+
178
+ // Create lib directory
179
+ const libPath = path.join(
180
+ props.cwd,
181
+ props.srcDir !== false ? "src" : "",
182
+ "lib",
183
+ );
184
+ const lib = await Folder(libPath);
185
+
186
+ // Create utils.ts file
187
+ const utilsContent = `
188
+ import { type ClassValue, clsx } from "clsx";
189
+ import { twMerge } from "tailwind-merge";
190
+
191
+ export function cn(...inputs: ClassValue[]) {
192
+ return twMerge(clsx(inputs));
193
+ }
194
+ `;
195
+
196
+ await StaticTypeScriptFile(path.join(libPath, "utils.ts"), utilsContent);
197
+
198
+ // Install clsx and tailwind-merge
199
+ await exec(`bun add clsx tailwind-merge`);
200
+
201
+ // Install requested components
202
+ if (props.components && props.components.length > 0) {
203
+ for (const componentName of props.components) {
204
+ await ShadcnComponent(`shadcn-component-${componentName}`, {
205
+ name: componentName,
206
+ cwd: props.cwd,
207
+ force: props.force,
208
+ silent: props.silent,
209
+ });
210
+ }
211
+ }
212
+
213
+ return this({
214
+ ...props,
215
+ ui,
216
+ lib,
217
+ });
218
+ },
219
+ );
@@ -0,0 +1,167 @@
1
+ import { exec } from "child_process";
2
+ import path from "path";
3
+ import { promisify } from "util";
4
+ import type { Context } from "../context";
5
+ import { Folder, StaticTextFile } from "../fs";
6
+ import { Resource } from "../resource";
7
+
8
+ const execAsync = promisify(exec);
9
+
10
+ /**
11
+ * Framework types supported by the Tailwind resource
12
+ */
13
+ export type TailwindFramework = "vite" | "astro" | "next" | "standalone";
14
+
15
+ /**
16
+ * Properties for Tailwind configuration
17
+ */
18
+ export interface TailwindConfigProps {
19
+ /**
20
+ * The working directory where Tailwind should be installed
21
+ */
22
+ cwd: string;
23
+
24
+ /**
25
+ * The framework being used (determines which dependencies to install)
26
+ * @default "standalone"
27
+ */
28
+ framework?: TailwindFramework;
29
+
30
+ /**
31
+ * The path to the CSS file where Tailwind directives should be added
32
+ * If not provided, a new file will be created at src/styles/base.css
33
+ */
34
+ cssPath?: string;
35
+
36
+ /**
37
+ * Custom additional packages to install with Tailwind
38
+ */
39
+ additionalPackages?: string[];
40
+ }
41
+
42
+ /**
43
+ * Tailwind configuration resource
44
+ */
45
+ export interface TailwindConfig
46
+ extends TailwindConfigProps,
47
+ Resource<"config::TailwindConfig"> {
48
+ /**
49
+ * The working directory where Tailwind is installed
50
+ */
51
+ cwd: string;
52
+ }
53
+
54
+ /**
55
+ * Installs and configures Tailwind CSS for a project
56
+ *
57
+ * @example
58
+ * // Install Tailwind for a Vite project
59
+ * const tailwind = await TailwindConfig("vite-tailwind", {
60
+ * cwd: "my-vite-app",
61
+ * framework: "vite"
62
+ * });
63
+ *
64
+ * @example
65
+ * // Install Tailwind for an Astro project
66
+ * const tailwind = await TailwindConfig("astro-tailwind", {
67
+ * cwd: "my-astro-app",
68
+ * framework: "astro"
69
+ * });
70
+ *
71
+ * @example
72
+ * // Install Tailwind as standalone with a custom CSS path
73
+ * const tailwind = await TailwindConfig("custom-tailwind", {
74
+ * cwd: "my-project",
75
+ * cssPath: "src/css/main.css",
76
+ * additionalPackages: ["@tailwindcss/typography", "@tailwindcss/forms"]
77
+ * });
78
+ */
79
+ export const TailwindConfig = Resource(
80
+ "config::TailwindConfig",
81
+ async function (
82
+ this: Context<TailwindConfig>,
83
+ id: string,
84
+ props: TailwindConfigProps,
85
+ ): Promise<TailwindConfig> {
86
+ if (this.phase === "delete") {
87
+ // Nothing to clean up specifically for Tailwind
88
+ return this.destroy();
89
+ }
90
+
91
+ const framework = props.framework || "standalone";
92
+
93
+ // Setup execAsync with the correct cwd
94
+ const exec = (command: string) => execAsync(command, { cwd: props.cwd });
95
+
96
+ // Install Tailwind and framework-specific dependencies
97
+ await installTailwindDependencies(framework);
98
+
99
+ // Create CSS file with Tailwind directives
100
+ await createTailwindCssFile();
101
+
102
+ return this({
103
+ ...props,
104
+ framework: framework,
105
+ });
106
+
107
+ /**
108
+ * Install Tailwind and framework-specific dependencies
109
+ */
110
+ async function installTailwindDependencies(framework: TailwindFramework) {
111
+ const basePackages = ["tailwindcss", "postcss", "autoprefixer"];
112
+
113
+ // Add framework-specific packages
114
+ const frameworkPackages = getFrameworkPackages(framework);
115
+
116
+ // Add custom additional packages
117
+ const additionalPackages = props.additionalPackages || [];
118
+
119
+ // Combine all packages
120
+ const allPackages = [
121
+ ...basePackages,
122
+ ...frameworkPackages,
123
+ ...additionalPackages,
124
+ ];
125
+
126
+ // Install all packages
127
+ await exec(`bun add -D ${allPackages.join(" ")}`);
128
+ }
129
+
130
+ /**
131
+ * Get framework-specific packages for Tailwind
132
+ */
133
+ function getFrameworkPackages(framework: TailwindFramework): string[] {
134
+ switch (framework) {
135
+ case "vite":
136
+ return ["@tailwindcss/vite"];
137
+ case "astro":
138
+ return ["@astrojs/tailwind"];
139
+ case "next":
140
+ return [];
141
+ case "standalone":
142
+ default:
143
+ return [];
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Create or update CSS file with Tailwind directives
149
+ */
150
+ async function createTailwindCssFile() {
151
+ const tailwindDirectives = `@import 'tailwindcss';`;
152
+
153
+ // Determine CSS file path
154
+ const cssPath =
155
+ props.cssPath || path.join(props.cwd, "src", "styles", "global.css");
156
+
157
+ // Create directory structure if needed
158
+ const cssDir = path.dirname(cssPath);
159
+ await Folder(path.join(id, "css-dir"), {
160
+ path: cssDir,
161
+ });
162
+
163
+ // Create CSS file with Tailwind directives
164
+ await StaticTextFile(cssPath, tailwindDirectives);
165
+ }
166
+ },
167
+ );
@@ -1,11 +1,12 @@
1
1
  import { exec } from "child_process";
2
- import fs from "fs/promises";
3
2
  import path from "path";
4
3
  import { promisify } from "util";
5
4
  import type { Context } from "../context";
5
+ import { Folder, StaticJsonFile, StaticTypeScriptFile } from "../fs";
6
6
  import { Resource } from "../resource";
7
- import { ShadcnComponent } from "../shadcn/component";
8
7
  import { rm } from "../util/rm";
8
+ import { ShadcnComponent } from "./shadcn-component";
9
+ import { TailwindConfig } from "./tailwind";
9
10
 
10
11
  const execAsync = promisify(exec);
11
12
 
@@ -113,6 +114,11 @@ export interface ViteProjectProps {
113
114
  * @default false
114
115
  */
115
116
  overwrite?: boolean;
117
+ /**
118
+ * Whether to delete the project folder during the delete phase
119
+ * @default true
120
+ */
121
+ delete?: boolean;
116
122
  }
117
123
 
118
124
  export interface ViteProject extends ViteProjectProps, Resource {
@@ -135,9 +141,8 @@ export const ViteProject = Resource(
135
141
  const phase = this.phase;
136
142
  if (this.phase === "delete") {
137
143
  try {
138
- if (await fs.exists(props.name)) {
139
- // TODO: OS agnostic - fs.rm is slow to delete node_modules/
140
- await execAsync("rm -rf " + props.name);
144
+ if (props.delete !== false) {
145
+ await execAsync(`rm -rf ${props.name}`);
141
146
  }
142
147
  } catch (error) {
143
148
  console.error(`Error deleting project ${id}:`, error);
@@ -174,6 +179,12 @@ export const ViteProject = Resource(
174
179
 
175
180
  const cwd = path.resolve(process.cwd(), props.name);
176
181
 
182
+ // Create folder for project
183
+ await Folder("project-dir", {
184
+ path: props.name,
185
+ delete: props.delete,
186
+ });
187
+
177
188
  const exec = (command: string) => execAsync(command, { cwd });
178
189
 
179
190
  if (phase === "create" || props.overwrite) {
@@ -202,9 +213,12 @@ export const ViteProject = Resource(
202
213
  }
203
214
 
204
215
  async function installTailwind() {
205
- await exec(`bun add tailwindcss @tailwindcss/vite`);
216
+ await TailwindConfig(`${id}-tailwind`, {
217
+ cwd: props.name,
218
+ framework: "vite",
219
+ });
206
220
 
207
- await fs.writeFile(
221
+ await StaticTypeScriptFile(
208
222
  path.join(props.name, "vite.config.ts"),
209
223
  `import path from "path";
210
224
  import react from "@vitejs/plugin-react";
@@ -222,16 +236,6 @@ export default defineConfig({
222
236
  },
223
237
  });`,
224
238
  );
225
-
226
- // Add Tailwind CSS import to index.css
227
- const indexCssPath = path.join(props.name, "src", "index.css");
228
- const currentCss = await fs.readFile(indexCssPath, "utf-8");
229
- if (!currentCss.includes('@import "tailwindcss";')) {
230
- await fs.writeFile(
231
- indexCssPath,
232
- '@import "tailwindcss";\n\n' + currentCss,
233
- );
234
- }
235
239
  }
236
240
 
237
241
  async function installShadcn() {
@@ -273,8 +277,14 @@ export default defineConfig({
273
277
 
274
278
  const src = path.join(props.name, "src");
275
279
  const routes = path.join(src, "routes");
276
- await fs.mkdir(routes, { recursive: true });
277
- await fs.writeFile(
280
+
281
+ // Create routes directory
282
+ await Folder("routes-dir", {
283
+ path: routes,
284
+ });
285
+
286
+ // Create root route file
287
+ await StaticTypeScriptFile(
278
288
  path.join(routes, "__root.tsx"),
279
289
  `import { Link, Outlet, createRootRoute } from "@tanstack/react-router";
280
290
  import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
@@ -299,7 +309,8 @@ export const Route = createRootRoute({
299
309
  `,
300
310
  );
301
311
 
302
- await fs.writeFile(
312
+ // Create index route file
313
+ await StaticTypeScriptFile(
303
314
  path.join(routes, "index.tsx"),
304
315
  `import { createLazyFileRoute } from '@tanstack/react-router'
305
316
 
@@ -316,7 +327,8 @@ function Index() {
316
327
  }`,
317
328
  );
318
329
 
319
- await fs.writeFile(
330
+ // Create about route file
331
+ await StaticTypeScriptFile(
320
332
  path.join(routes, "about.tsx"),
321
333
  `import { createLazyFileRoute } from '@tanstack/react-router'
322
334
 
@@ -329,7 +341,8 @@ function About() {
329
341
  }`,
330
342
  );
331
343
 
332
- await fs.writeFile(
344
+ // Create main.tsx
345
+ await StaticTypeScriptFile(
333
346
  path.join(src, "main.tsx"),
334
347
  `import { StrictMode } from 'react'
335
348
  import ReactDOM from 'react-dom/client'
@@ -363,43 +376,28 @@ if (!rootElement.innerHTML) {
363
376
  }
364
377
 
365
378
  async function removeUnnecessaryFiles() {
366
- await Promise.all([
367
- rm(path.join(props.name, "src", "App.tsx")),
368
- rm(path.join(props.name, "src", "App.css")),
369
- ]);
379
+ await rm(path.join(props.name, "src", "App.tsx"));
380
+ await rm(path.join(props.name, "src", "App.css"));
370
381
  }
371
382
 
372
383
  async function patchTsConfig() {
373
- await Promise.all([
374
- rm(path.join(props.name, "tsconfig.app.json")),
375
- rm(path.join(props.name, "tsconfig.node.json")),
376
- fs.writeFile(
377
- path.join(props.name, "tsconfig.json"),
378
- JSON.stringify(
379
- {
380
- extends: props.extends,
381
- compilerOptions: {
382
- baseUrl: ".",
383
- paths: {
384
- "@/*": ["./src/*"],
385
- },
386
- types: ["@cloudflare/workers-types"],
387
- allowImportingTsExtensions: true,
388
- jsx: "react-jsx",
389
- },
390
- include: [
391
- "vite/*.ts",
392
- "src/**/*.ts",
393
- "src/**/*.tsx",
394
- "src/env.d.ts",
395
- ],
396
- references: props.references?.map((path) => ({ path })),
397
- },
398
- null,
399
- 2,
400
- ),
401
- ),
402
- ]);
384
+ await rm(path.join(props.name, "tsconfig.app.json"));
385
+ await rm(path.join(props.name, "tsconfig.node.json"));
386
+
387
+ await StaticJsonFile(path.join(props.name, "tsconfig.json"), {
388
+ extends: props.extends,
389
+ compilerOptions: {
390
+ baseUrl: ".",
391
+ paths: {
392
+ "@/*": ["./src/*"],
393
+ },
394
+ types: ["@cloudflare/workers-types"],
395
+ allowImportingTsExtensions: true,
396
+ jsx: "react-jsx",
397
+ },
398
+ include: ["vite/*.ts", "src/**/*.ts", "src/**/*.tsx", "src/env.d.ts"],
399
+ references: props.references?.map((path) => ({ path })),
400
+ });
403
401
  }
404
402
  }
405
403
  },
@@ -0,0 +1,48 @@
1
+ import path from "path";
2
+ import type { DefaultTheme, ThemeOptions } from "vitepress";
3
+ import { StaticTypeScriptFile } from "../../fs/static-typescript-file";
4
+
5
+ export type VitePressConfigProps = {
6
+ cwd: string;
7
+ title?: string;
8
+ description?: string;
9
+ theme?: ThemeOptions;
10
+ themeConfig: DefaultTheme.Config;
11
+ };
12
+
13
+ export type VitePressConfig = Awaited<ReturnType<typeof VitePressConfig>>;
14
+
15
+ export function VitePressConfig(props: VitePressConfigProps) {
16
+ return StaticTypeScriptFile(
17
+ path.join(props.cwd, ".vitepress", "config.mts"),
18
+ `import { transformerTwoslash } from "@shikijs/vitepress-twoslash";
19
+ import footnotePlugin from "markdown-it-footnote";
20
+ import { defineConfig } from "vitepress";
21
+
22
+ // https://vitepress.dev/reference/site-config
23
+ export default defineConfig({
24
+ title: ${JSON.stringify(props.title || "Alchemy")},
25
+ description: ${JSON.stringify(props.description || "Alchemy Docs")},
26
+ markdown: {
27
+ // @ts-ignore
28
+ codeTransformers: [transformerTwoslash()],
29
+ theme: ${JSON.stringify(
30
+ props.theme ?? {
31
+ light: "light-plus",
32
+ dark: "dark-plus",
33
+ }
34
+ )},
35
+ config: (md) => md.use(footnotePlugin),
36
+ },
37
+ // https://vitepress.dev/reference/default-theme-config
38
+ themeConfig: ${JSON.stringify({
39
+ ...props.themeConfig,
40
+ search: props.themeConfig.search ?? { provider: "local" },
41
+ nav: props.themeConfig.nav ?? [{ text: "Home", link: "/" }],
42
+ sidebar: props.themeConfig.sidebar ?? [],
43
+ socialLinks: props.themeConfig.socialLinks ?? [],
44
+ })}
45
+ });
46
+ `
47
+ );
48
+ }