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.
- package/README.md +3 -3
- package/lib/ai/approve.d.ts +99 -0
- package/lib/ai/approve.js +76 -0
- package/lib/ai/astro-file.d.ts +137 -0
- package/lib/ai/astro-file.js +156 -0
- package/lib/ai/client.d.ts +7 -8
- package/lib/ai/client.js +48 -19
- package/lib/ai/css-file.d.ts +131 -0
- package/lib/ai/css-file.js +141 -0
- package/lib/ai/data.d.ts +51 -6
- package/lib/ai/data.js +58 -12
- package/lib/ai/document.d.ts +108 -24
- package/lib/ai/document.js +166 -52
- package/lib/ai/html-file.d.ts +128 -0
- package/lib/ai/html-file.js +138 -0
- package/lib/ai/index.d.ts +9 -0
- package/lib/ai/index.js +9 -0
- package/lib/ai/json-file.d.ts +160 -0
- package/lib/ai/json-file.js +187 -0
- package/lib/ai/review.d.ts +122 -0
- package/lib/ai/review.js +101 -0
- package/lib/ai/typescript-file.d.ts +139 -0
- package/lib/ai/typescript-file.js +145 -0
- package/lib/ai/vue-file.d.ts +126 -0
- package/lib/ai/vue-file.js +136 -0
- package/lib/ai/yaml-file.d.ts +169 -0
- package/lib/ai/yaml-file.js +200 -0
- package/lib/alchemy.js +17 -2
- package/lib/apply.js +1 -1
- package/lib/cloudflare/dns.d.ts +88 -0
- package/lib/cloudflare/dns.js +192 -0
- package/lib/cloudflare/generate-asset-manifest.js +1 -1
- package/lib/cloudflare/index.d.ts +2 -1
- package/lib/cloudflare/index.js +2 -1
- package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
- package/lib/cloudflare/r2-rest-state-store.js +254 -0
- package/lib/cloudflare/response.d.ts +12 -0
- package/lib/cloudflare/response.js +0 -0
- package/lib/cloudflare/static-site-router.js +16 -7
- package/lib/cloudflare/static-site.js +3 -0
- package/lib/cloudflare/upload-asset-manifest.js +4 -2
- package/lib/cloudflare/zone.d.ts +18 -0
- package/lib/cloudflare/zone.js +17 -4
- package/lib/dns/godaddy.d.ts +7 -0
- package/lib/dns/godaddy.js +21 -0
- package/lib/dns/import-dns.d.ts +82 -0
- package/lib/dns/import-dns.js +114 -0
- package/lib/dns/index.d.ts +2 -0
- package/lib/dns/index.js +2 -0
- package/lib/dns/record.d.ts +88 -0
- package/lib/dns/record.js +13 -0
- package/lib/fs/copy-file.d.ts +54 -0
- package/lib/fs/copy-file.js +63 -0
- package/lib/fs/file-system-state-store.d.ts +17 -0
- package/lib/fs/file-system-state-store.js +87 -0
- package/lib/fs/file.d.ts +13 -0
- package/lib/fs/file.js +24 -5
- package/lib/fs/folder.d.ts +10 -0
- package/lib/fs/folder.js +2 -2
- package/lib/fs/index.d.ts +10 -4
- package/lib/fs/index.js +10 -4
- package/lib/fs/static-astro-file.d.ts +34 -0
- package/lib/fs/static-astro-file.js +39 -0
- package/lib/fs/static-css-file.d.ts +24 -0
- package/lib/fs/static-css-file.js +29 -0
- package/lib/fs/static-html-file.d.ts +31 -0
- package/lib/fs/static-html-file.js +36 -0
- package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +3 -3
- package/lib/fs/static-json-file.js +15 -0
- package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
- package/lib/fs/static-text-file.js +8 -0
- package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +3 -3
- package/lib/fs/{typescript-file.js → static-typescript-file.js} +3 -2
- package/lib/fs/static-vue-file.d.ts +28 -0
- package/lib/fs/static-vue-file.js +33 -0
- package/lib/fs/static-yaml-file.d.ts +22 -0
- package/lib/fs/{yaml-file.js → static-yaml-file.js} +3 -2
- package/lib/internal/getting-started.d.ts +21 -0
- package/lib/internal/getting-started.js +87 -0
- package/lib/internal/index.d.ts +3 -0
- package/lib/internal/index.js +3 -0
- package/lib/internal/providers.d.ts +27 -0
- package/lib/internal/providers.js +172 -0
- package/lib/internal/tutorial.d.ts +104 -0
- package/lib/internal/tutorial.js +251 -0
- package/lib/resource.js +8 -1
- package/lib/scope.d.ts +1 -1
- package/lib/scope.js +4 -2
- package/lib/state.d.ts +1 -16
- package/lib/state.js +0 -87
- package/lib/test/bun.d.ts +11 -0
- package/lib/test/bun.js +36 -8
- package/lib/web/astro.d.ts +147 -0
- package/lib/web/astro.js +414 -0
- package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
- package/lib/web/shadcn.d.ts +97 -0
- package/lib/web/shadcn.js +99 -0
- package/lib/web/tailwind.d.ts +64 -0
- package/lib/web/tailwind.js +97 -0
- package/lib/{vite → web}/vite.d.ts +5 -0
- package/lib/{vite → web}/vite.js +44 -44
- package/lib/web/vitepress/config.d.ts +10 -0
- package/lib/web/vitepress/config.js +31 -0
- package/lib/web/vitepress/custom-theme.d.ts +173 -0
- package/lib/web/vitepress/custom-theme.js +336 -0
- package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
- package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
- package/lib/web/vitepress/home-page.d.ts +246 -0
- package/lib/web/vitepress/home-page.js +147 -0
- package/lib/{vitepress → web/vitepress}/index.d.ts +2 -0
- package/lib/{vitepress → web/vitepress}/index.js +2 -0
- package/lib/{vitepress → web/vitepress}/vitepress.d.ts +15 -12
- package/lib/{vitepress → web/vitepress}/vitepress.js +26 -44
- package/package.json +6 -2
- package/src/ai/approve.ts +163 -0
- package/src/ai/astro-file.ts +269 -0
- package/src/ai/client.ts +60 -21
- package/src/ai/css-file.ts +247 -0
- package/src/ai/data.ts +81 -19
- package/src/ai/document.ts +240 -62
- package/src/ai/html-file.ts +244 -0
- package/src/ai/index.ts +9 -0
- package/src/ai/json-file.ts +325 -0
- package/src/ai/review.ts +213 -0
- package/src/ai/typescript-file.ts +259 -0
- package/src/ai/vue-file.ts +242 -0
- package/src/ai/yaml-file.ts +328 -0
- package/src/alchemy.ts +18 -7
- package/src/apply.ts +8 -9
- package/src/cloudflare/dns.ts +344 -0
- package/src/cloudflare/generate-asset-manifest.ts +4 -4
- package/src/cloudflare/index.ts +2 -1
- package/src/cloudflare/r2-rest-state-store.ts +353 -0
- package/src/cloudflare/response.ts +9 -0
- package/src/cloudflare/static-site-router.ts +18 -8
- package/src/cloudflare/static-site.ts +11 -7
- package/src/cloudflare/upload-asset-manifest.ts +6 -4
- package/src/cloudflare/worker.ts +44 -43
- package/src/cloudflare/zone.ts +39 -23
- package/src/dns/godaddy.ts +29 -0
- package/src/dns/import-dns.ts +213 -0
- package/src/dns/index.ts +2 -0
- package/src/dns/record.ts +121 -0
- package/src/fs/copy-file.ts +115 -0
- package/src/fs/file-system-state-store.ts +109 -0
- package/src/fs/file.ts +35 -9
- package/src/fs/folder.ts +14 -2
- package/src/fs/index.ts +10 -4
- package/src/fs/static-astro-file.ts +45 -0
- package/src/fs/static-css-file.ts +35 -0
- package/src/fs/static-html-file.ts +42 -0
- package/src/fs/static-json-file.ts +34 -0
- package/src/fs/{text-file.ts → static-text-file.ts} +9 -3
- package/src/fs/{typescript-file.ts → static-typescript-file.ts} +7 -6
- package/src/fs/static-vue-file.ts +39 -0
- package/src/fs/static-yaml-file.ts +33 -0
- package/src/internal/getting-started.ts +115 -0
- package/src/internal/index.ts +3 -0
- package/src/internal/providers.ts +241 -0
- package/src/internal/tutorial.ts +392 -0
- package/src/resource.ts +10 -1
- package/src/scope.ts +5 -6
- package/src/state.ts +1 -111
- package/src/test/bun.ts +60 -10
- package/src/web/astro.ts +644 -0
- package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
- package/src/web/shadcn.ts +219 -0
- package/src/web/tailwind.ts +167 -0
- package/src/{vite → web}/vite.ts +54 -56
- package/src/web/vitepress/config.ts +48 -0
- package/src/web/vitepress/custom-theme.ts +514 -0
- package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
- package/src/web/vitepress/home-page.ts +375 -0
- package/src/{vitepress → web/vitepress}/index.ts +2 -0
- package/src/{vitepress → web/vitepress}/vitepress.ts +62 -72
- package/lib/cloudflare/state.d.ts +0 -82
- package/lib/cloudflare/state.js +0 -108
- package/lib/fs/json-file.js +0 -7
- package/lib/fs/text-file.js +0 -7
- package/lib/fs/yaml-file.d.ts +0 -19
- package/lib/internal/docs.d.ts +0 -5
- package/lib/internal/docs.js +0 -287
- package/lib/shadcn/index.d.ts +0 -1
- package/lib/shadcn/index.js +0 -1
- package/lib/vite/index.d.ts +0 -1
- package/lib/vite/index.js +0 -1
- package/lib/vitepress/home-page.d.ts +0 -133
- package/lib/vitepress/home-page.js +0 -11
- package/src/cloudflare/state.ts +0 -138
- package/src/fs/json-file.ts +0 -23
- package/src/fs/yaml-file.ts +0 -26
- package/src/internal/docs.ts +0 -338
- package/src/shadcn/index.ts +0 -1
- package/src/vite/index.ts +0 -1
- package/src/vitepress/home-page.ts +0 -166
- /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
- /package/src/{vitepress → web/vitepress}/index.md +0 -0
package/lib/test/bun.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Scope } from "../scope";
|
|
2
|
+
import type { StateStoreType } from "../state";
|
|
2
3
|
/**
|
|
3
4
|
* Extend the Alchemy interface to include test functionality
|
|
4
5
|
*/
|
|
@@ -26,6 +27,14 @@ export interface TestOptions {
|
|
|
26
27
|
* @default "test-password".
|
|
27
28
|
*/
|
|
28
29
|
password?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Override the default state store for the test.
|
|
32
|
+
*/
|
|
33
|
+
stateStore?: StateStoreType;
|
|
34
|
+
/**
|
|
35
|
+
* Prefix to use for the scope to isolate tests and environments.
|
|
36
|
+
*/
|
|
37
|
+
prefix?: string;
|
|
29
38
|
}
|
|
30
39
|
/**
|
|
31
40
|
* Test function type definition with overloads
|
|
@@ -51,6 +60,8 @@ type test = {
|
|
|
51
60
|
* @param condition If true, test will be skipped
|
|
52
61
|
*/
|
|
53
62
|
skipIf(condition: boolean): test;
|
|
63
|
+
beforeAll(fn: (scope: Scope) => Promise<void>): void;
|
|
64
|
+
afterAll(fn: (scope: Scope) => Promise<void>): void;
|
|
54
65
|
/**
|
|
55
66
|
* Current test scope
|
|
56
67
|
*/
|
package/lib/test/bun.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/// <reference types="bun" />
|
|
2
|
-
import { afterAll, it } from "bun:test";
|
|
2
|
+
import { afterAll, beforeAll, it } from "bun:test";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { alchemy } from "../alchemy";
|
|
5
|
+
import { R2RestStateStore } from "../cloudflare";
|
|
5
6
|
import { destroy } from "../destroy";
|
|
6
7
|
/**
|
|
7
8
|
* Add test functionality to alchemy instance
|
|
8
9
|
*/
|
|
9
10
|
alchemy.test = test;
|
|
10
|
-
/**
|
|
11
|
-
* Global test scope for all tests
|
|
12
|
-
*/
|
|
13
|
-
const globalTestScope = alchemy.scope("test");
|
|
14
11
|
/**
|
|
15
12
|
* Creates a test helper function that provides scoped resource management
|
|
16
13
|
*
|
|
@@ -35,6 +32,16 @@ const globalTestScope = alchemy.scope("test");
|
|
|
35
32
|
* ```
|
|
36
33
|
*/
|
|
37
34
|
export function test(meta, defaultOptions) {
|
|
35
|
+
defaultOptions = defaultOptions ?? {};
|
|
36
|
+
if (defaultOptions.stateStore === undefined &&
|
|
37
|
+
// process.env.CI &&
|
|
38
|
+
process.env.ALCHEMY_STATE_STORE === "cloudflare") {
|
|
39
|
+
defaultOptions.stateStore = (scope) => new R2RestStateStore(scope, {
|
|
40
|
+
apiKey: process.env.CLOUDFLARE_API_KEY,
|
|
41
|
+
email: process.env.CLOUDFLARE_EMAIL,
|
|
42
|
+
bucketName: process.env.CLOUDFLARE_BUCKET_NAME,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
38
45
|
// Add skipIf functionality
|
|
39
46
|
test.skipIf = (condition) => {
|
|
40
47
|
if (condition) {
|
|
@@ -44,12 +51,29 @@ export function test(meta, defaultOptions) {
|
|
|
44
51
|
return test;
|
|
45
52
|
};
|
|
46
53
|
// Create local test scope based on filename
|
|
47
|
-
const localTestScope = alchemy.scope(path.basename(meta.filename)
|
|
48
|
-
parent: globalTestScope,
|
|
54
|
+
const localTestScope = alchemy.scope(`${defaultOptions.prefix ? `${defaultOptions.prefix}-` : ""}${path.basename(meta.filename)}`, {
|
|
55
|
+
// parent: globalTestScope,
|
|
56
|
+
stateStore: defaultOptions?.stateStore,
|
|
49
57
|
});
|
|
50
58
|
test.scope = localTestScope;
|
|
59
|
+
test.beforeAll = (fn) => {
|
|
60
|
+
return beforeAll(async () => {
|
|
61
|
+
test.scope.enter();
|
|
62
|
+
await fn(test.scope);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
test.afterAll = (fn) => {
|
|
66
|
+
return afterAll(async () => {
|
|
67
|
+
test.scope.enter();
|
|
68
|
+
await fn(test.scope);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
51
71
|
// Clean up test scope after all tests complete
|
|
52
|
-
afterAll(() =>
|
|
72
|
+
afterAll(async () => {
|
|
73
|
+
if (defaultOptions?.destroy !== false) {
|
|
74
|
+
await alchemy.destroy(test.scope);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
53
77
|
return test;
|
|
54
78
|
function test(...args) {
|
|
55
79
|
const testName = args[0];
|
|
@@ -76,6 +100,10 @@ export function test(meta, defaultOptions) {
|
|
|
76
100
|
try {
|
|
77
101
|
await fn(scope);
|
|
78
102
|
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.error(err);
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
79
107
|
finally {
|
|
80
108
|
if (options.destroy !== false) {
|
|
81
109
|
await destroy(scope);
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
/**
|
|
4
|
+
* Type of integrations that can be added to an Astro project
|
|
5
|
+
*/
|
|
6
|
+
export type AstroIntegration = "react" | "preact" | "vue" | "svelte" | "solid" | "lit" | "tailwind" | "mdx" | "sitemap" | "partytown" | "markdoc";
|
|
7
|
+
/**
|
|
8
|
+
* Properties for creating an Astro project
|
|
9
|
+
*/
|
|
10
|
+
export interface AstroProjectProps {
|
|
11
|
+
/**
|
|
12
|
+
* The name/path of the project
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* The title of the site
|
|
17
|
+
* @default "Astro Site"
|
|
18
|
+
*/
|
|
19
|
+
title?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The description of the site
|
|
22
|
+
* @default "Welcome to my Astro site"
|
|
23
|
+
*/
|
|
24
|
+
description?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The directory to initialize the project in.
|
|
27
|
+
* @default {@link name}
|
|
28
|
+
*/
|
|
29
|
+
dir?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The integrations to add to the project
|
|
32
|
+
* @default []
|
|
33
|
+
*/
|
|
34
|
+
integrations?: AstroIntegration[];
|
|
35
|
+
/**
|
|
36
|
+
* The TypeScript configuration
|
|
37
|
+
*/
|
|
38
|
+
tsconfig?: {
|
|
39
|
+
/**
|
|
40
|
+
* Extends from this TypeScript configuration
|
|
41
|
+
*/
|
|
42
|
+
extends?: string;
|
|
43
|
+
/**
|
|
44
|
+
* References to add to the tsconfig
|
|
45
|
+
*/
|
|
46
|
+
references?: string[];
|
|
47
|
+
/**
|
|
48
|
+
* Compiler options to add to the tsconfig
|
|
49
|
+
*/
|
|
50
|
+
compilerOptions?: Record<string, any>;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Whether to delete the project folder during the delete phase
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
delete?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Add Shadcn UI to the project
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
shadcn?: {
|
|
62
|
+
/**
|
|
63
|
+
* The base color to use
|
|
64
|
+
* @default "neutral"
|
|
65
|
+
*/
|
|
66
|
+
baseColor?: "neutral" | "gray" | "zinc" | "stone" | "slate";
|
|
67
|
+
/**
|
|
68
|
+
* Use default configuration
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
defaults?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Force overwrite of existing configuration
|
|
74
|
+
* @default false
|
|
75
|
+
*/
|
|
76
|
+
force?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Mute output
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
silent?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Use the src directory when creating a new project
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
srcDir?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Use css variables for theming
|
|
89
|
+
* @default true
|
|
90
|
+
*/
|
|
91
|
+
cssVariables?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The components to add
|
|
94
|
+
*/
|
|
95
|
+
components?: string[];
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The dependencies to install
|
|
99
|
+
*/
|
|
100
|
+
dependencies?: Record<string, string>;
|
|
101
|
+
/**
|
|
102
|
+
* The dev dependencies to install
|
|
103
|
+
*/
|
|
104
|
+
devDependencies?: Record<string, string>;
|
|
105
|
+
/**
|
|
106
|
+
* Additional scripts to add to package.json
|
|
107
|
+
*/
|
|
108
|
+
scripts?: Record<string, string>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Astro project resource
|
|
112
|
+
*/
|
|
113
|
+
export interface AstroProject extends AstroProjectProps, Resource {
|
|
114
|
+
/**
|
|
115
|
+
* The name/path of the project
|
|
116
|
+
*/
|
|
117
|
+
name: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Creates a new Astro project
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // Create a basic Astro project
|
|
124
|
+
* const basicProject = await AstroProject("my-astro-app", {
|
|
125
|
+
* title: "My Astro Site",
|
|
126
|
+
* description: "Built with Alchemy"
|
|
127
|
+
* });
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Create an Astro project with React and Tailwind
|
|
131
|
+
* const reactProject = await AstroProject("astro-react", {
|
|
132
|
+
* title: "Astro + React",
|
|
133
|
+
* integrations: ["react", "tailwind"]
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // Create an Astro project with Shadcn UI
|
|
138
|
+
* const shadcnProject = await AstroProject("astro-shadcn", {
|
|
139
|
+
* title: "Astro with Shadcn UI",
|
|
140
|
+
* integrations: ["react", "tailwind"],
|
|
141
|
+
* shadcn: {
|
|
142
|
+
* baseColor: "zinc",
|
|
143
|
+
* components: ["button", "card", "input"]
|
|
144
|
+
* }
|
|
145
|
+
* });
|
|
146
|
+
*/
|
|
147
|
+
export declare const AstroProject: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<AstroProject>, id: string, props: AstroProjectProps) => Promise<AstroProject>);
|
package/lib/web/astro.js
ADDED
|
@@ -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
|
-
|
|
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
|