alchemy 0.2.5 → 0.3.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 (151) hide show
  1. package/README.md +3 -3
  2. package/lib/ai/astro-file.d.ts +137 -0
  3. package/lib/ai/astro-file.js +173 -0
  4. package/lib/ai/client.d.ts +2 -10
  5. package/lib/ai/client.js +8 -24
  6. package/lib/ai/css-file.d.ts +131 -0
  7. package/lib/ai/css-file.js +158 -0
  8. package/lib/ai/data.d.ts +45 -5
  9. package/lib/ai/data.js +45 -10
  10. package/lib/ai/document.d.ts +44 -20
  11. package/lib/ai/document.js +95 -28
  12. package/lib/ai/html-file.d.ts +128 -0
  13. package/lib/ai/html-file.js +155 -0
  14. package/lib/ai/index.d.ts +7 -0
  15. package/lib/ai/index.js +7 -0
  16. package/lib/ai/json-file.d.ts +160 -0
  17. package/lib/ai/json-file.js +208 -0
  18. package/lib/ai/typescript-file.d.ts +140 -0
  19. package/lib/ai/typescript-file.js +176 -0
  20. package/lib/ai/vue-file.d.ts +126 -0
  21. package/lib/ai/vue-file.js +153 -0
  22. package/lib/ai/yaml-file.d.ts +169 -0
  23. package/lib/ai/yaml-file.js +236 -0
  24. package/lib/cloudflare/dns.d.ts +88 -0
  25. package/lib/cloudflare/dns.js +192 -0
  26. package/lib/cloudflare/index.d.ts +2 -1
  27. package/lib/cloudflare/index.js +2 -1
  28. package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
  29. package/lib/cloudflare/r2-rest-state-store.js +254 -0
  30. package/lib/cloudflare/response.d.ts +12 -0
  31. package/lib/cloudflare/response.js +0 -0
  32. package/lib/cloudflare/zone.d.ts +18 -0
  33. package/lib/cloudflare/zone.js +17 -4
  34. package/lib/dns/godaddy.d.ts +7 -0
  35. package/lib/dns/godaddy.js +21 -0
  36. package/lib/dns/import-dns.d.ts +82 -0
  37. package/lib/dns/import-dns.js +114 -0
  38. package/lib/dns/index.d.ts +2 -0
  39. package/lib/dns/index.js +2 -0
  40. package/lib/dns/record.d.ts +88 -0
  41. package/lib/dns/record.js +13 -0
  42. package/lib/fs/file-system-state-store.d.ts +17 -0
  43. package/lib/fs/file-system-state-store.js +87 -0
  44. package/lib/fs/folder.d.ts +10 -0
  45. package/lib/fs/folder.js +2 -2
  46. package/lib/fs/index.d.ts +4 -4
  47. package/lib/fs/index.js +4 -4
  48. package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +2 -2
  49. package/lib/fs/static-json-file.js +14 -0
  50. package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
  51. package/lib/fs/{text-file.js → static-text-file.js} +1 -1
  52. package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +2 -2
  53. package/lib/fs/{typescript-file.js → static-typescript-file.js} +1 -1
  54. package/lib/fs/{yaml-file.d.ts → static-yaml-file.d.ts} +2 -2
  55. package/lib/fs/{yaml-file.js → static-yaml-file.js} +1 -1
  56. package/lib/internal/getting-started.d.ts +10 -0
  57. package/lib/internal/getting-started.js +94 -0
  58. package/lib/internal/project.d.ts +13 -0
  59. package/lib/internal/project.js +101 -0
  60. package/lib/internal/providers.d.ts +23 -0
  61. package/lib/internal/providers.js +148 -0
  62. package/lib/resource.js +8 -1
  63. package/lib/scope.d.ts +1 -1
  64. package/lib/scope.js +4 -2
  65. package/lib/state.d.ts +1 -16
  66. package/lib/state.js +0 -87
  67. package/lib/test/bun.d.ts +11 -0
  68. package/lib/test/bun.js +36 -8
  69. package/lib/web/astro.d.ts +147 -0
  70. package/lib/web/astro.js +414 -0
  71. package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
  72. package/lib/web/shadcn.d.ts +97 -0
  73. package/lib/web/shadcn.js +99 -0
  74. package/lib/web/tailwind.d.ts +64 -0
  75. package/lib/web/tailwind.js +97 -0
  76. package/lib/{vite → web}/vite.d.ts +5 -0
  77. package/lib/{vite → web}/vite.js +44 -44
  78. package/lib/web/vitepress/custom-theme.d.ts +173 -0
  79. package/lib/web/vitepress/custom-theme.js +336 -0
  80. package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
  81. package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
  82. package/lib/web/vitepress/home-page.d.ts +244 -0
  83. package/lib/web/vitepress/home-page.js +134 -0
  84. package/lib/{vitepress → web/vitepress}/index.d.ts +1 -0
  85. package/lib/{vitepress → web/vitepress}/index.js +1 -0
  86. package/lib/{vitepress → web/vitepress}/vitepress.d.ts +10 -8
  87. package/lib/{vitepress → web/vitepress}/vitepress.js +11 -12
  88. package/package.json +6 -2
  89. package/src/ai/astro-file.ts +288 -0
  90. package/src/ai/client.ts +8 -28
  91. package/src/ai/css-file.ts +266 -0
  92. package/src/ai/data.ts +53 -16
  93. package/src/ai/document.ts +130 -33
  94. package/src/ai/html-file.ts +263 -0
  95. package/src/ai/index.ts +7 -0
  96. package/src/ai/json-file.ts +349 -0
  97. package/src/ai/typescript-file.ts +293 -0
  98. package/src/ai/vue-file.ts +261 -0
  99. package/src/ai/yaml-file.ts +368 -0
  100. package/src/apply.ts +0 -1
  101. package/src/cloudflare/dns.ts +344 -0
  102. package/src/cloudflare/index.ts +2 -1
  103. package/src/cloudflare/r2-rest-state-store.ts +353 -0
  104. package/src/cloudflare/response.ts +9 -0
  105. package/src/cloudflare/zone.ts +39 -23
  106. package/src/dns/godaddy.ts +29 -0
  107. package/src/dns/import-dns.ts +213 -0
  108. package/src/dns/index.ts +2 -0
  109. package/src/dns/record.ts +121 -0
  110. package/src/fs/file-system-state-store.ts +109 -0
  111. package/src/fs/folder.ts +14 -2
  112. package/src/fs/index.ts +4 -4
  113. package/src/fs/{json-file.ts → static-json-file.ts} +13 -3
  114. package/src/fs/{text-file.ts → static-text-file.ts} +5 -2
  115. package/src/fs/{typescript-file.ts → static-typescript-file.ts} +3 -3
  116. package/src/fs/{yaml-file.ts → static-yaml-file.ts} +5 -2
  117. package/src/internal/getting-started.ts +107 -0
  118. package/src/internal/project.ts +119 -0
  119. package/src/internal/providers.ts +209 -0
  120. package/src/resource.ts +10 -1
  121. package/src/scope.ts +5 -6
  122. package/src/state.ts +1 -111
  123. package/src/test/bun.ts +60 -10
  124. package/src/web/astro.ts +644 -0
  125. package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
  126. package/src/web/shadcn.ts +219 -0
  127. package/src/web/tailwind.ts +167 -0
  128. package/src/{vite → web}/vite.ts +54 -56
  129. package/src/web/vitepress/custom-theme.ts +514 -0
  130. package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
  131. package/src/web/vitepress/home-page.ts +363 -0
  132. package/src/{vitepress → web/vitepress}/index.ts +1 -0
  133. package/src/{vitepress → web/vitepress}/vitepress.ts +34 -27
  134. package/lib/cloudflare/state.d.ts +0 -82
  135. package/lib/cloudflare/state.js +0 -108
  136. package/lib/fs/json-file.js +0 -7
  137. package/lib/internal/docs.d.ts +0 -5
  138. package/lib/internal/docs.js +0 -287
  139. package/lib/shadcn/index.d.ts +0 -1
  140. package/lib/shadcn/index.js +0 -1
  141. package/lib/vite/index.d.ts +0 -1
  142. package/lib/vite/index.js +0 -1
  143. package/lib/vitepress/home-page.d.ts +0 -133
  144. package/lib/vitepress/home-page.js +0 -11
  145. package/src/cloudflare/state.ts +0 -138
  146. package/src/internal/docs.ts +0 -338
  147. package/src/shadcn/index.ts +0 -1
  148. package/src/vite/index.ts +0 -1
  149. package/src/vitepress/home-page.ts +0 -166
  150. /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
  151. /package/src/{vitepress → web/vitepress}/index.md +0 -0
@@ -0,0 +1,414 @@
1
+ import { exec } from "child_process";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import { promisify } from "util";
5
+ import { Folder, StaticJsonFile, StaticTextFile, StaticTypeScriptFile, } from "../fs";
6
+ import { Resource } from "../resource";
7
+ import { ShadcnUI } from "./shadcn";
8
+ const execAsync = promisify(exec);
9
+ /**
10
+ * Creates a new Astro project
11
+ *
12
+ * @example
13
+ * // Create a basic Astro project
14
+ * const basicProject = await AstroProject("my-astro-app", {
15
+ * title: "My Astro Site",
16
+ * description: "Built with Alchemy"
17
+ * });
18
+ *
19
+ * @example
20
+ * // Create an Astro project with React and Tailwind
21
+ * const reactProject = await AstroProject("astro-react", {
22
+ * title: "Astro + React",
23
+ * integrations: ["react", "tailwind"]
24
+ * });
25
+ *
26
+ * @example
27
+ * // Create an Astro project with Shadcn UI
28
+ * const shadcnProject = await AstroProject("astro-shadcn", {
29
+ * title: "Astro with Shadcn UI",
30
+ * integrations: ["react", "tailwind"],
31
+ * shadcn: {
32
+ * baseColor: "zinc",
33
+ * components: ["button", "card", "input"]
34
+ * }
35
+ * });
36
+ */
37
+ export const AstroProject = Resource("project::AstroProject", {
38
+ alwaysUpdate: true,
39
+ }, async function (id, props) {
40
+ const dir = props.dir ?? props.name;
41
+ if (this.phase === "delete") {
42
+ try {
43
+ if (props.delete !== false) {
44
+ if (await fs.stat(dir).catch(() => null)) {
45
+ await execAsync(`rm -rf ${dir}`);
46
+ }
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error(`Error deleting project ${id}:`, error);
51
+ }
52
+ return this.destroy();
53
+ }
54
+ const cwd = path.resolve(process.cwd(), dir);
55
+ // Create the project directory
56
+ await Folder("project-dir", {
57
+ path: dir,
58
+ delete: true,
59
+ });
60
+ await Folder(".astro", {
61
+ path: dir,
62
+ delete: true,
63
+ clean: true,
64
+ });
65
+ await setupProject(props);
66
+ return this(props);
67
+ /**
68
+ * Set up the Astro project structure and configuration
69
+ */
70
+ async function setupProject(props) {
71
+ // Create project structure
72
+ await ProjectStructure();
73
+ // Set up package.json
74
+ await StaticJsonFile(path.join(dir, "package.json"), {
75
+ name: props.name,
76
+ type: "module",
77
+ version: "0.0.1",
78
+ scripts: {
79
+ dev: "astro dev",
80
+ start: "astro dev",
81
+ build: "astro build",
82
+ preview: "astro preview",
83
+ astro: "astro",
84
+ ...props.scripts,
85
+ },
86
+ dependencies: props.dependencies || {},
87
+ devDependencies: props.devDependencies || {},
88
+ });
89
+ // Set up tsconfig.json
90
+ await StaticJsonFile(path.join(dir, "tsconfig.json"), {
91
+ extends: props.tsconfig?.extends || "astro/tsconfigs/strict",
92
+ compilerOptions: {
93
+ baseUrl: ".",
94
+ paths: {
95
+ "@/*": ["./src/*"],
96
+ },
97
+ jsx: "react-jsx",
98
+ jsxImportSource: "react",
99
+ ...props.tsconfig?.compilerOptions,
100
+ },
101
+ include: ["src/**/*.ts", "src/**/*.tsx", "src/**/*.astro"],
102
+ references: props.tsconfig?.references?.map((path) => ({ path })),
103
+ });
104
+ // Set up astro.config.mjs
105
+ await AstroConfig();
106
+ // Set up initial files
107
+ await InitialFiles();
108
+ // Install integrations
109
+ if (props.integrations && props.integrations.length > 0) {
110
+ await InstallIntegrations();
111
+ }
112
+ // Install dependencies
113
+ await InstallDependencies();
114
+ // Set up Shadcn if requested
115
+ if (props.shadcn) {
116
+ await ShadcnUI(id, {
117
+ ...props.shadcn,
118
+ cwd: dir,
119
+ });
120
+ }
121
+ }
122
+ /**
123
+ * Create the basic project structure
124
+ */
125
+ async function ProjectStructure() {
126
+ const folderPaths = [
127
+ "src",
128
+ "src/components",
129
+ "src/layouts",
130
+ "src/pages",
131
+ "public",
132
+ ];
133
+ for (const folderPath of folderPaths) {
134
+ await Folder(`${dir}-${folderPath}`, {
135
+ path: path.join(dir, folderPath),
136
+ });
137
+ }
138
+ }
139
+ /**
140
+ * Set up astro.config.mjs
141
+ */
142
+ async function AstroConfig() {
143
+ const integrations = [];
144
+ const vitePlugins = [];
145
+ // Add imports and integrations
146
+ const configImports = ['import { defineConfig } from "astro/config";'];
147
+ if (props.integrations?.includes("react")) {
148
+ configImports.push('import react from "@astrojs/react";');
149
+ integrations.push("react()");
150
+ }
151
+ if (props.integrations?.includes("preact")) {
152
+ configImports.push('import preact from "@astrojs/preact";');
153
+ integrations.push("preact()");
154
+ }
155
+ if (props.integrations?.includes("vue")) {
156
+ configImports.push('import vue from "@astrojs/vue";');
157
+ integrations.push("vue()");
158
+ }
159
+ if (props.integrations?.includes("svelte")) {
160
+ configImports.push('import svelte from "@astrojs/svelte";');
161
+ integrations.push("svelte()");
162
+ }
163
+ if (props.integrations?.includes("solid")) {
164
+ configImports.push('import solid from "@astrojs/solid-js";');
165
+ integrations.push("solid()");
166
+ }
167
+ if (props.integrations?.includes("lit")) {
168
+ configImports.push('import lit from "@astrojs/lit";');
169
+ integrations.push("lit()");
170
+ }
171
+ if (props.integrations?.includes("tailwind")) {
172
+ configImports.push('import tailwindcss from "@tailwindcss/vite";');
173
+ vitePlugins.push("tailwindcss()");
174
+ }
175
+ if (props.integrations?.includes("mdx")) {
176
+ configImports.push('import mdx from "@astrojs/mdx";');
177
+ integrations.push("mdx()");
178
+ }
179
+ if (props.integrations?.includes("sitemap")) {
180
+ configImports.push('import sitemap from "@astrojs/sitemap";');
181
+ integrations.push("sitemap()");
182
+ }
183
+ if (props.integrations?.includes("partytown")) {
184
+ configImports.push('import partytown from "@astrojs/partytown";');
185
+ integrations.push("partytown()");
186
+ }
187
+ if (props.integrations?.includes("markdoc")) {
188
+ configImports.push('import markdoc from "@astrojs/markdoc";');
189
+ integrations.push("markdoc()");
190
+ }
191
+ const integrationsStr = integrations.length
192
+ ? `\n integrations: [${integrations.join(", ")}],`
193
+ : "";
194
+ const vitePluginsStr = vitePlugins.length
195
+ ? `\n vite: {\n plugins: [${vitePlugins.join(", ")}]\n },`
196
+ : "";
197
+ return StaticTypeScriptFile(path.join(dir, "astro.config.ts"), `${configImports.join("\n")}
198
+
199
+ // https://astro.build/config
200
+ export default defineConfig({${integrationsStr}${vitePluginsStr}
201
+ site: "https://example.com",
202
+ output: "static"
203
+ });`);
204
+ }
205
+ /**
206
+ * Create initial files for the project
207
+ */
208
+ async function InitialFiles() {
209
+ // Create .gitignore
210
+ await StaticTextFile(path.join(dir, ".gitignore"), `# build output
211
+ dist/
212
+ .output/
213
+
214
+ # dependencies
215
+ node_modules/
216
+
217
+ # logs
218
+ npm-debug.log*
219
+ yarn-debug.log*
220
+ yarn-error.log*
221
+ pnpm-debug.log*
222
+
223
+ # environment variables
224
+ .env
225
+ .env.production
226
+
227
+ # macOS-specific files
228
+ .DS_Store
229
+ .astro/
230
+ `);
231
+ // Create .env.d.ts
232
+ await StaticTextFile(path.join(dir, "src", "env.d.ts"), `/// <reference types="astro/client" />
233
+ `);
234
+ // Create a basic layout
235
+ await StaticTextFile(path.join(dir, "src", "layouts", "Layout.astro"), `---
236
+ interface Props {
237
+ title: string;
238
+ description?: string;
239
+ }
240
+
241
+ const { title, description = "Welcome to my Astro site" } = Astro.props;
242
+ ---
243
+
244
+ <!DOCTYPE html>
245
+ <html lang="en">
246
+ <head>
247
+ <meta charset="UTF-8" />
248
+ <meta name="viewport" content="width=device-width" />
249
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
250
+ <meta name="generator" content={Astro.generator} />
251
+ <meta name="description" content={description} />
252
+ <title>{title}</title>
253
+ </head>
254
+ <body>
255
+ <slot />
256
+ </body>
257
+ </html>
258
+
259
+ <style is:global>
260
+ :root {
261
+ --accent: 124, 58, 237;
262
+ --accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
263
+ }
264
+ html {
265
+ font-family: system-ui, sans-serif;
266
+ background-color: #f6f6f6;
267
+ }
268
+ code {
269
+ font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
270
+ Bitstream Vera Sans Mono, Courier New, monospace;
271
+ }
272
+ </style>
273
+ `);
274
+ // Create a basic index page
275
+ await StaticTextFile(path.join(dir, "src", "pages", "index.astro"), `---
276
+ import Layout from '../layouts/Layout.astro';
277
+ ---
278
+
279
+ <Layout title="${props.title || "Astro Site"}" description="${props.description || "Welcome to my Astro site"}">
280
+ <main>
281
+ <h1><span class="text-gradient">Astro</span> Site</h1>
282
+ <p class="instructions">
283
+ To get started, edit <code>src/pages/index.astro</code> and save to see your changes.
284
+ </p>
285
+ </main>
286
+ </Layout>
287
+
288
+ <style>
289
+ main {
290
+ margin: auto;
291
+ padding: 1.5rem;
292
+ max-width: 60ch;
293
+ }
294
+ h1 {
295
+ font-size: 3rem;
296
+ font-weight: 800;
297
+ margin: 0;
298
+ }
299
+ .text-gradient {
300
+ background-image: var(--accent-gradient);
301
+ -webkit-background-clip: text;
302
+ -webkit-text-fill-color: transparent;
303
+ background-size: 400%;
304
+ background-position: 0%;
305
+ }
306
+ .instructions {
307
+ line-height: 1.6;
308
+ margin: 1rem 0;
309
+ border: 1px solid rgba(var(--accent), 25%);
310
+ background-color: white;
311
+ padding: 1rem;
312
+ border-radius: 0.4rem;
313
+ }
314
+ .instructions code {
315
+ font-size: 0.875em;
316
+ font-weight: bold;
317
+ background: rgba(var(--accent), 12%);
318
+ color: rgb(var(--accent));
319
+ border-radius: 4px;
320
+ padding: 0.3em 0.45em;
321
+ }
322
+ </style>
323
+ `);
324
+ // Create public/favicon.svg
325
+ await StaticTextFile(path.join(dir, "public", "favicon.svg"), `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 36 36">
326
+ <path fill="#000" d="M22.25 4h-8.5a1 1 0 0 0-.96.73l-5.54 19.4a.5.5 0 0 0 .62.62l5.05-1.44a2 2 0 0 0 1.38-1.4l3.22-11.66a.5.5 0 0 1 .96 0l3.22 11.67a2 2 0 0 0 1.38 1.39l5.05 1.44a.5.5 0 0 0 .62-.62l-5.54-19.4a1 1 0 0 0-.96-.73Z"/>
327
+ <path fill="url(#gradient)" d="M18 28a7.63 7.63 0 0 1-5-2c-1.4 2.1-.35 4.35.6 5.55.14.17.41.07.47-.15.44-1.8 2.93-1.22 2.93.6 0 2.28.87 3.4 1.72 3.81.34.16.59-.2.49-.56-.31-1.05-.29-2.46 1.29-3.25 3-1.5 3.17-4.83 2.5-6-.67.67-2.6 2-5 2Z"/>
328
+ <defs>
329
+ <linearGradient id="gradient" x1="16" x2="16" y1="32" y2="24" gradientUnits="userSpaceOnUse">
330
+ <stop stop-color="#FF1639"/>
331
+ <stop offset="1" stop-color="#FF1639" stop-opacity="0"/>
332
+ </linearGradient>
333
+ </defs>
334
+ </svg>
335
+ `);
336
+ }
337
+ /**
338
+ * Install Astro integrations
339
+ */
340
+ async function InstallIntegrations() {
341
+ const exec = (command) => execAsync(command, { cwd });
342
+ const integrationPackages = [];
343
+ for (const integration of props.integrations) {
344
+ switch (integration) {
345
+ case "react":
346
+ integrationPackages.push("@astrojs/react", "react", "react-dom");
347
+ break;
348
+ case "preact":
349
+ integrationPackages.push("@astrojs/preact", "preact");
350
+ break;
351
+ case "vue":
352
+ integrationPackages.push("@astrojs/vue", "vue");
353
+ break;
354
+ case "svelte":
355
+ integrationPackages.push("@astrojs/svelte", "svelte");
356
+ break;
357
+ case "solid":
358
+ integrationPackages.push("@astrojs/solid-js", "solid-js");
359
+ break;
360
+ case "lit":
361
+ integrationPackages.push("@astrojs/lit", "lit");
362
+ break;
363
+ case "tailwind":
364
+ integrationPackages.push("@tailwindcss/vite", "tailwindcss");
365
+ // Create a base.css file with @tailwind directives
366
+ await Folder(path.join(dir, "src", "styles"));
367
+ await StaticTextFile(path.join(dir, "src", "styles", "global.css"), `@tailwind base;
368
+ @tailwind components;
369
+ @tailwind utilities;
370
+ `);
371
+ break;
372
+ case "mdx":
373
+ integrationPackages.push("@astrojs/mdx");
374
+ break;
375
+ case "sitemap":
376
+ integrationPackages.push("@astrojs/sitemap");
377
+ break;
378
+ case "partytown":
379
+ integrationPackages.push("@astrojs/partytown");
380
+ break;
381
+ case "markdoc":
382
+ integrationPackages.push("@astrojs/markdoc");
383
+ break;
384
+ }
385
+ }
386
+ if (integrationPackages.length > 0) {
387
+ await exec(`bun add astro ${integrationPackages.join(" ")}`);
388
+ }
389
+ }
390
+ /**
391
+ * Install dependencies
392
+ */
393
+ async function InstallDependencies() {
394
+ const exec = (command) => execAsync(command, { cwd });
395
+ // Install Astro and general dependencies
396
+ await exec(`bun add astro`);
397
+ // Install dev dependencies
398
+ const devDepsEntries = Object.entries(props.devDependencies || {});
399
+ if (devDepsEntries.length > 0) {
400
+ const devDepsArg = devDepsEntries
401
+ .map(([name, version]) => `${name}@${version}`)
402
+ .join(" ");
403
+ await exec(`bun add -D ${devDepsArg}`);
404
+ }
405
+ // Install regular dependencies
406
+ const depsEntries = Object.entries(props.dependencies || {});
407
+ if (depsEntries.length > 0) {
408
+ const depsArg = depsEntries
409
+ .map(([name, version]) => `${name}@${version}`)
410
+ .join(" ");
411
+ await exec(`bun add ${depsArg}`);
412
+ }
413
+ }
414
+ });
@@ -3,18 +3,11 @@ import fs from "fs/promises";
3
3
  import path from "path";
4
4
  import { promisify } from "util";
5
5
  import { Resource } from "../resource";
6
+ import { ignore } from "../util/ignore";
6
7
  const execAsync = promisify(exec);
7
8
  export const ShadcnComponent = Resource("project::ShadcnComponent", async function (id, props) {
8
9
  if (this.phase === "delete") {
9
- const componentPath = path.join(props.cwd, "src", "components", "ui", `${props.name}.tsx`);
10
- try {
11
- await fs.unlink(componentPath);
12
- }
13
- catch (error) {
14
- if (error.code !== "ENOENT") {
15
- throw error;
16
- }
17
- }
10
+ await ignore("ENOENT", () => fs.unlink(path.join(props.cwd, "src", "components", "ui", `${props.name}.tsx`)));
18
11
  return this.destroy();
19
12
  }
20
13
  // Build the shadcn add command with options
@@ -0,0 +1,97 @@
1
+ import type { Context } from "../context";
2
+ import { Folder } from "../fs";
3
+ import { Resource } from "../resource";
4
+ /**
5
+ * Properties for initializing Shadcn UI
6
+ */
7
+ export interface ShadcnUIProps {
8
+ /**
9
+ * The working directory where Shadcn UI should be installed
10
+ */
11
+ cwd: string;
12
+ /**
13
+ * The base color to use
14
+ * @default "neutral"
15
+ */
16
+ baseColor?: "neutral" | "gray" | "zinc" | "stone" | "slate";
17
+ /**
18
+ * Use default configuration
19
+ * @default false
20
+ */
21
+ defaults?: boolean;
22
+ /**
23
+ * Force overwrite of existing configuration
24
+ * @default false
25
+ */
26
+ force?: boolean;
27
+ /**
28
+ * Mute output
29
+ * @default false
30
+ */
31
+ silent?: boolean;
32
+ /**
33
+ * Use the src directory when creating a new project
34
+ * @default true
35
+ */
36
+ srcDir?: boolean;
37
+ /**
38
+ * Use css variables for theming
39
+ * @default true
40
+ */
41
+ cssVariables?: boolean;
42
+ /**
43
+ * The components to add
44
+ */
45
+ components?: string[];
46
+ /**
47
+ * Whether tailwind is already installed and configured.
48
+ * Shadcn UI requires Tailwind CSS to be installed and configured.
49
+ * @default false
50
+ */
51
+ tailwind?: boolean;
52
+ /**
53
+ * Whether react is already installed
54
+ * @default false
55
+ */
56
+ react?: boolean;
57
+ }
58
+ /**
59
+ * Shadcn UI resource
60
+ */
61
+ export interface ShadcnUI extends ShadcnUIProps, Resource<"project::ShadcnUI"> {
62
+ /**
63
+ * The working directory where Shadcn UI is installed
64
+ */
65
+ cwd: string;
66
+ /**
67
+ * The ui directory
68
+ */
69
+ ui: Folder;
70
+ /**
71
+ * The lib directory
72
+ */
73
+ lib: Folder;
74
+ }
75
+ /**
76
+ * Initializes Shadcn UI in a project directory
77
+ *
78
+ * @example
79
+ * // Initialize Shadcn UI with default settings
80
+ * const shadcn = await ShadcnUI("my-shadcn", {
81
+ * cwd: "my-project",
82
+ * tailwind: true, // Tailwind must be installed separately
83
+ * react: true // React must be installed separately
84
+ * });
85
+ *
86
+ * @example
87
+ * // Initialize Shadcn UI with custom settings and components
88
+ * const shadcn = await ShadcnUI("custom-shadcn", {
89
+ * cwd: "my-project",
90
+ * baseColor: "zinc",
91
+ * force: true,
92
+ * components: ["button", "card", "input"],
93
+ * tailwind: true, // Tailwind must be installed separately
94
+ * react: true // React must be installed separately
95
+ * });
96
+ */
97
+ export declare const ShadcnUI: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<ShadcnUI>, id: string, props: ShadcnUIProps) => Promise<ShadcnUI>);
@@ -0,0 +1,99 @@
1
+ import { exec } from "child_process";
2
+ import path from "path";
3
+ import { promisify } from "util";
4
+ import { Folder, StaticJsonFile } from "../fs";
5
+ import { StaticTypeScriptFile } from "../fs/static-typescript-file";
6
+ import { Resource } from "../resource";
7
+ import { ShadcnComponent } from "./shadcn-component";
8
+ const execAsync = promisify(exec);
9
+ /**
10
+ * Initializes Shadcn UI in a project directory
11
+ *
12
+ * @example
13
+ * // Initialize Shadcn UI with default settings
14
+ * const shadcn = await ShadcnUI("my-shadcn", {
15
+ * cwd: "my-project",
16
+ * tailwind: true, // Tailwind must be installed separately
17
+ * react: true // React must be installed separately
18
+ * });
19
+ *
20
+ * @example
21
+ * // Initialize Shadcn UI with custom settings and components
22
+ * const shadcn = await ShadcnUI("custom-shadcn", {
23
+ * cwd: "my-project",
24
+ * baseColor: "zinc",
25
+ * force: true,
26
+ * components: ["button", "card", "input"],
27
+ * tailwind: true, // Tailwind must be installed separately
28
+ * react: true // React must be installed separately
29
+ * });
30
+ */
31
+ export const ShadcnUI = Resource("project::ShadcnUI", async function (id, props) {
32
+ if (this.phase === "delete") {
33
+ // For a delete phase, we don't perform any action
34
+ // as removing Shadcn UI would require removing many files
35
+ // which could be destructive to the project
36
+ console.log("Note: ShadcnUI delete phase does not remove installed components");
37
+ return this.destroy();
38
+ }
39
+ // Setup execAsync with the correct cwd
40
+ const exec = (command) => execAsync(command, { cwd: props.cwd });
41
+ // Ensure React is installed if not already
42
+ if (!props.react) {
43
+ await exec(`bun add react react-dom`);
44
+ }
45
+ // Install shadcn dependencies
46
+ await exec(`bun add -D @types/node`);
47
+ // Create components.json directly instead of running shadcn init
48
+ await StaticJsonFile(path.join(props.cwd, "components.json"), {
49
+ $schema: "https://ui.shadcn.com/schema.json",
50
+ style: props.cssVariables !== false ? "default" : "new-york",
51
+ tsx: true,
52
+ rsc: false,
53
+ tailwind: {
54
+ config: "tailwind.config.js",
55
+ css: props.srcDir !== false
56
+ ? "src/styles/globals.css"
57
+ : "styles/globals.css",
58
+ baseColor: props.baseColor || "neutral",
59
+ cssVariables: props.cssVariables !== false,
60
+ },
61
+ aliases: {
62
+ components: props.srcDir !== false ? "@/components" : "./components",
63
+ utils: props.srcDir !== false ? "@/lib/utils" : "./lib/utils",
64
+ },
65
+ });
66
+ // Ensure ui components directory exists
67
+ const ui = await Folder(path.join(props.cwd, props.srcDir !== false ? "src" : "", "components", "ui"));
68
+ // Create lib directory
69
+ const libPath = path.join(props.cwd, props.srcDir !== false ? "src" : "", "lib");
70
+ const lib = await Folder(libPath);
71
+ // Create utils.ts file
72
+ const utilsContent = `
73
+ import { type ClassValue, clsx } from "clsx";
74
+ import { twMerge } from "tailwind-merge";
75
+
76
+ export function cn(...inputs: ClassValue[]) {
77
+ return twMerge(clsx(inputs));
78
+ }
79
+ `;
80
+ await StaticTypeScriptFile(path.join(libPath, "utils.ts"), utilsContent);
81
+ // Install clsx and tailwind-merge
82
+ await exec(`bun add clsx tailwind-merge`);
83
+ // Install requested components
84
+ if (props.components && props.components.length > 0) {
85
+ for (const componentName of props.components) {
86
+ await ShadcnComponent(`shadcn-component-${componentName}`, {
87
+ name: componentName,
88
+ cwd: props.cwd,
89
+ force: props.force,
90
+ silent: props.silent,
91
+ });
92
+ }
93
+ }
94
+ return this({
95
+ ...props,
96
+ ui,
97
+ lib,
98
+ });
99
+ });
@@ -0,0 +1,64 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ /**
4
+ * Framework types supported by the Tailwind resource
5
+ */
6
+ export type TailwindFramework = "vite" | "astro" | "next" | "standalone";
7
+ /**
8
+ * Properties for Tailwind configuration
9
+ */
10
+ export interface TailwindConfigProps {
11
+ /**
12
+ * The working directory where Tailwind should be installed
13
+ */
14
+ cwd: string;
15
+ /**
16
+ * The framework being used (determines which dependencies to install)
17
+ * @default "standalone"
18
+ */
19
+ framework?: TailwindFramework;
20
+ /**
21
+ * The path to the CSS file where Tailwind directives should be added
22
+ * If not provided, a new file will be created at src/styles/base.css
23
+ */
24
+ cssPath?: string;
25
+ /**
26
+ * Custom additional packages to install with Tailwind
27
+ */
28
+ additionalPackages?: string[];
29
+ }
30
+ /**
31
+ * Tailwind configuration resource
32
+ */
33
+ export interface TailwindConfig extends TailwindConfigProps, Resource<"config::TailwindConfig"> {
34
+ /**
35
+ * The working directory where Tailwind is installed
36
+ */
37
+ cwd: string;
38
+ }
39
+ /**
40
+ * Installs and configures Tailwind CSS for a project
41
+ *
42
+ * @example
43
+ * // Install Tailwind for a Vite project
44
+ * const tailwind = await TailwindConfig("vite-tailwind", {
45
+ * cwd: "my-vite-app",
46
+ * framework: "vite"
47
+ * });
48
+ *
49
+ * @example
50
+ * // Install Tailwind for an Astro project
51
+ * const tailwind = await TailwindConfig("astro-tailwind", {
52
+ * cwd: "my-astro-app",
53
+ * framework: "astro"
54
+ * });
55
+ *
56
+ * @example
57
+ * // Install Tailwind as standalone with a custom CSS path
58
+ * const tailwind = await TailwindConfig("custom-tailwind", {
59
+ * cwd: "my-project",
60
+ * cssPath: "src/css/main.css",
61
+ * additionalPackages: ["@tailwindcss/typography", "@tailwindcss/forms"]
62
+ * });
63
+ */
64
+ export declare const TailwindConfig: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<TailwindConfig>, id: string, props: TailwindConfigProps) => Promise<TailwindConfig>);