alchemy 0.10.1 → 0.10.3
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/lib/alchemy.js +4 -1
- package/lib/cloudflare/api.js +1 -1
- package/lib/cloudflare/assets.js +2 -2
- package/lib/cloudflare/bound.d.ts +1 -1
- package/lib/cloudflare/d1-database.d.ts +28 -1
- package/lib/cloudflare/d1-database.js +39 -1
- package/lib/cloudflare/d1-migrations.d.ts +46 -0
- package/lib/cloudflare/d1-migrations.js +82 -0
- package/lib/cloudflare/worker.js +2 -2
- package/lib/cloudflare/wrangler.json.js +1 -1
- package/lib/internal/docs/providers.d.ts +2 -2
- package/lib/internal/docs/providers.js +5 -4
- package/lib/test/bun.js +1 -1
- package/lib/test/prune.d.ts +13 -0
- package/lib/test/prune.js +130 -0
- package/lib/util/content-type.js +1 -1
- package/lib/util/rm.js +1 -1
- package/lib/web/astro.js +6 -3
- package/lib/web/shadcn-component.js +2 -2
- package/lib/web/shadcn.d.ts +1 -1
- package/lib/web/shadcn.js +3 -2
- package/lib/web/tailwind.js +3 -2
- package/lib/web/vite.js +5 -3
- package/lib/web/vitepress/config.js +1 -1
- package/lib/web/vitepress/custom-theme.js +2 -2
- package/lib/web/vitepress/home-page.d.ts +1 -1
- package/lib/web/vitepress/home-page.js +1 -1
- package/lib/web/vitepress/vitepress.js +7 -4
- package/package.json +1 -1
- package/src/alchemy.ts +4 -1
- package/src/cloudflare/api.ts +1 -1
- package/src/cloudflare/assets.ts +2 -2
- package/src/cloudflare/bound.ts +3 -1
- package/src/cloudflare/d1-database.ts +65 -1
- package/src/cloudflare/d1-migrations.ts +137 -0
- package/src/cloudflare/worker.ts +2 -2
- package/src/cloudflare/wrangler.json.ts +1 -1
- package/src/internal/docs/providers.ts +5 -4
- package/src/test/bun.ts +1 -1
- package/src/test/prune.ts +165 -0
- package/src/util/content-type.ts +1 -1
- package/src/util/rm.ts +1 -1
- package/src/web/astro.ts +15 -17
- package/src/web/shadcn-component.ts +6 -6
- package/src/web/shadcn.ts +9 -8
- package/src/web/tailwind.ts +5 -4
- package/src/web/vite.ts +14 -12
- package/src/web/vitepress/config.ts +1 -1
- package/src/web/vitepress/custom-theme.ts +9 -9
- package/src/web/vitepress/home-page.ts +21 -17
- package/src/web/vitepress/vitepress.ts +7 -9
package/src/alchemy.ts
CHANGED
|
@@ -111,7 +111,10 @@ async function _alchemy(
|
|
|
111
111
|
: 0;
|
|
112
112
|
const indent = " ".repeat(leadingSpaces);
|
|
113
113
|
|
|
114
|
-
const {
|
|
114
|
+
const [{ isFileRef }, { isFileCollection }] = await Promise.all([
|
|
115
|
+
import("./fs/file-ref"),
|
|
116
|
+
import("./fs/file-collection"),
|
|
117
|
+
]);
|
|
115
118
|
|
|
116
119
|
const appendices: Record<string, string> = {};
|
|
117
120
|
|
package/src/cloudflare/api.ts
CHANGED
|
@@ -131,7 +131,7 @@ export class CloudflareApi {
|
|
|
131
131
|
errorMsg.includes("ETIMEDOUT") ||
|
|
132
132
|
errorMsg.includes("ECONNREFUSED");
|
|
133
133
|
|
|
134
|
-
return isNetworkError;
|
|
134
|
+
return isNetworkError || error.status?.toString().startsWith("5");
|
|
135
135
|
},
|
|
136
136
|
5, // Maximum 5 attempts (1 initial + 4 retries)
|
|
137
137
|
1000 // Start with 1s delay, will exponentially increase
|
package/src/cloudflare/assets.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
3
|
import type { Context } from "../context";
|
|
4
4
|
import { Resource } from "../resource";
|
|
5
5
|
import { getContentType } from "../util/content-type";
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type CloudflareApiOptions,
|
|
7
7
|
} from "./api";
|
|
8
8
|
import { CloudflareApiError, handleApiError } from "./api-error";
|
|
9
|
+
import { applyMigrations, listMigrationsFiles } from "./d1-migrations";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Properties for creating or updating a D1 Database
|
|
@@ -57,6 +58,25 @@ export interface D1DatabaseProps extends CloudflareApiOptions {
|
|
|
57
58
|
* @default false
|
|
58
59
|
*/
|
|
59
60
|
adopt?: boolean;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* These files will be generated internally with the D1Database wrapper function when migrationsDir is specified
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
migrationsFiles?: Array<{ id: string; sql: string }>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Name of the table used to track migrations. Only used if migrationsDir is specified. Defaults to 'd1_migrations'
|
|
71
|
+
* This is analogous to wrangler's `migrations_table`.
|
|
72
|
+
*/
|
|
73
|
+
migrationsTable?: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Directory containing migration SQL files. If not set, no migrations will be applied.
|
|
77
|
+
* This is analogous to wrangler's `migrations_dir`.
|
|
78
|
+
*/
|
|
79
|
+
migrationsDir?: string;
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
/**
|
|
@@ -97,6 +117,20 @@ export interface D1Database
|
|
|
97
117
|
};
|
|
98
118
|
}
|
|
99
119
|
|
|
120
|
+
export async function D1Database(
|
|
121
|
+
id: string,
|
|
122
|
+
props: Omit<D1DatabaseProps, "migrationsFiles">
|
|
123
|
+
) {
|
|
124
|
+
const migrationsFiles = props.migrationsDir
|
|
125
|
+
? await listMigrationsFiles(props.migrationsDir)
|
|
126
|
+
: [];
|
|
127
|
+
|
|
128
|
+
return D1DatabaseResource(id, {
|
|
129
|
+
...props,
|
|
130
|
+
migrationsFiles,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
100
134
|
/**
|
|
101
135
|
* Creates and manages Cloudflare D1 Databases.
|
|
102
136
|
*
|
|
@@ -126,9 +160,16 @@ export interface D1Database
|
|
|
126
160
|
* }
|
|
127
161
|
* });
|
|
128
162
|
*
|
|
163
|
+
* @example
|
|
164
|
+
* // Create a database with migrations
|
|
165
|
+
* const dbWithMigrations = await D1Database("mydb", {
|
|
166
|
+
* name: "mydb",
|
|
167
|
+
* migrationsDir: "./migrations",
|
|
168
|
+
* });
|
|
169
|
+
*
|
|
129
170
|
* @see https://developers.cloudflare.com/d1/
|
|
130
171
|
*/
|
|
131
|
-
export const
|
|
172
|
+
export const D1DatabaseResource = Resource(
|
|
132
173
|
"cloudflare::D1Database",
|
|
133
174
|
async function (
|
|
134
175
|
this: Context<D1Database>,
|
|
@@ -204,6 +245,29 @@ export const D1Database = Resource(
|
|
|
204
245
|
}
|
|
205
246
|
}
|
|
206
247
|
|
|
248
|
+
// Run migrations if provided
|
|
249
|
+
if (props.migrationsFiles && props.migrationsFiles.length > 0) {
|
|
250
|
+
try {
|
|
251
|
+
const migrationsTable = props.migrationsTable || "d1_migrations";
|
|
252
|
+
const databaseId = dbData.result.uuid || this.output?.id;
|
|
253
|
+
|
|
254
|
+
if (!databaseId) {
|
|
255
|
+
throw new Error("Database ID not found for migrations");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await applyMigrations({
|
|
259
|
+
migrationsFiles: props.migrationsFiles,
|
|
260
|
+
migrationsTable,
|
|
261
|
+
accountId: api.accountId,
|
|
262
|
+
databaseId,
|
|
263
|
+
api,
|
|
264
|
+
});
|
|
265
|
+
} catch (migrationErr) {
|
|
266
|
+
console.error("Failed to apply D1 migrations:", migrationErr);
|
|
267
|
+
throw migrationErr;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
207
271
|
return this({
|
|
208
272
|
type: "d1",
|
|
209
273
|
id: dbData.result.uuid || "",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { CloudflareApi } from "./api";
|
|
4
|
+
import { handleApiError } from "./api-error";
|
|
5
|
+
|
|
6
|
+
export interface D1MigrationOptions {
|
|
7
|
+
migrationsFiles: Array<{ id: string; sql: string }>;
|
|
8
|
+
migrationsTable: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
databaseId: string;
|
|
11
|
+
api: CloudflareApi;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const getPrefix = (name: string) => {
|
|
15
|
+
const prefix = name.split("_")[0];
|
|
16
|
+
const num = parseInt(prefix, 10);
|
|
17
|
+
return isNaN(num) ? null : num;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async function readMigrationFile(filePath: string): Promise<string> {
|
|
21
|
+
return fs.readFile(filePath, "utf-8");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reads migration SQL files from the migrationsDir, sorted by filename.
|
|
26
|
+
* @param migrationsDir Directory containing .sql migration files
|
|
27
|
+
*/
|
|
28
|
+
export async function listMigrationsFiles(
|
|
29
|
+
migrationsDir: string
|
|
30
|
+
): Promise<Array<{ id: string; sql: string }>> {
|
|
31
|
+
const entries = await fs.readdir(migrationsDir);
|
|
32
|
+
|
|
33
|
+
const sqlFiles = entries
|
|
34
|
+
.filter((f: string) => f.endsWith(".sql"))
|
|
35
|
+
.sort((a: string, b: string) => {
|
|
36
|
+
const aNum = getPrefix(a);
|
|
37
|
+
const bNum = getPrefix(b);
|
|
38
|
+
|
|
39
|
+
if (aNum !== null && bNum !== null) return aNum - bNum;
|
|
40
|
+
if (aNum !== null) return -1;
|
|
41
|
+
if (bNum !== null) return 1;
|
|
42
|
+
|
|
43
|
+
return a.localeCompare(b);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const files: Array<{ id: string; sql: string }> = [];
|
|
47
|
+
for (const file of sqlFiles) {
|
|
48
|
+
const sql = await readMigrationFile(path.join(migrationsDir, file));
|
|
49
|
+
files.push({ id: file, sql });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return files;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Ensures the migrations table exists in the D1 database.
|
|
57
|
+
*/
|
|
58
|
+
export async function ensureMigrationsTable(
|
|
59
|
+
options: D1MigrationOptions
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
const createTableSQL = `CREATE TABLE IF NOT EXISTS ${options.migrationsTable} (id TEXT PRIMARY KEY, applied_at TEXT);`;
|
|
62
|
+
|
|
63
|
+
await executeD1SQL(options, createTableSQL);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets the list of applied migration IDs from the migrations table.
|
|
68
|
+
*/
|
|
69
|
+
export async function getAppliedMigrations(
|
|
70
|
+
options: D1MigrationOptions
|
|
71
|
+
): Promise<Set<string>> {
|
|
72
|
+
const sql = `SELECT id FROM ${options.migrationsTable};`;
|
|
73
|
+
|
|
74
|
+
const result = await executeD1SQL(options, sql);
|
|
75
|
+
|
|
76
|
+
const ids = (result?.result[0]?.results || []).map((row: any) => row.id);
|
|
77
|
+
return new Set(ids);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Executes a SQL statement against the D1 database using the HTTP API.
|
|
82
|
+
*/
|
|
83
|
+
export async function executeD1SQL(
|
|
84
|
+
options: D1MigrationOptions,
|
|
85
|
+
sql: string
|
|
86
|
+
): Promise<{
|
|
87
|
+
result: [
|
|
88
|
+
{
|
|
89
|
+
results: Array<unknown>;
|
|
90
|
+
success: boolean;
|
|
91
|
+
meta: any;
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
errors: Array<any>;
|
|
95
|
+
messages: Array<any>;
|
|
96
|
+
success: boolean;
|
|
97
|
+
}> {
|
|
98
|
+
const response = await options.api.post(
|
|
99
|
+
`/accounts/${options.accountId}/d1/database/${options.databaseId}/query`,
|
|
100
|
+
{ sql }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
await handleApiError(
|
|
105
|
+
response,
|
|
106
|
+
"executing migration SQL",
|
|
107
|
+
"D1 database",
|
|
108
|
+
options.databaseId
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return response.json();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Applies all pending migrations from the provided files to the D1 database.
|
|
117
|
+
*/
|
|
118
|
+
export async function applyMigrations(
|
|
119
|
+
options: D1MigrationOptions
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
await ensureMigrationsTable(options);
|
|
122
|
+
const applied = await getAppliedMigrations(options);
|
|
123
|
+
|
|
124
|
+
for (const migration of options.migrationsFiles) {
|
|
125
|
+
const migrationId = migration.id;
|
|
126
|
+
|
|
127
|
+
if (applied.has(migrationId)) continue;
|
|
128
|
+
|
|
129
|
+
// Run the migration
|
|
130
|
+
await executeD1SQL(options, migration.sql);
|
|
131
|
+
// Record as applied
|
|
132
|
+
const insertSQL = `INSERT INTO ${options.migrationsTable} (id, applied_at) VALUES ('${migrationId.replace("'", "''")}', datetime('now'));`;
|
|
133
|
+
await executeD1SQL(options, insertSQL);
|
|
134
|
+
|
|
135
|
+
console.log(`Applied migration: ${migrationId}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as crypto from "crypto";
|
|
2
|
-
import * as fs from "fs/promises";
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
3
|
import type { Context } from "../context";
|
|
4
4
|
import { Bundle, type BundleProps } from "../esbuild/bundle";
|
|
5
5
|
import { Resource } from "../resource";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "../context";
|
|
2
|
-
import { StaticJsonFile } from "../fs";
|
|
2
|
+
import { StaticJsonFile } from "../fs/static-json-file";
|
|
3
3
|
import { Resource } from "../resource";
|
|
4
4
|
import type { Bindings } from "./bindings";
|
|
5
5
|
import type { DurableObjectNamespace } from "./durable-object-namespace";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type } from "arktype";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { Data
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Data } from "../../ai/data";
|
|
5
|
+
import { Document } from "../../ai/document";
|
|
5
6
|
import { alchemy } from "../../alchemy";
|
|
6
|
-
import { Folder } from "../../fs";
|
|
7
|
+
import { Folder } from "../../fs/folder";
|
|
7
8
|
|
|
8
9
|
export interface DocsProps {
|
|
9
10
|
/**
|
package/src/test/bun.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { afterAll, beforeAll, it } from "bun:test";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { alchemy } from "../alchemy";
|
|
6
|
-
import { R2RestStateStore } from "../cloudflare";
|
|
6
|
+
import { R2RestStateStore } from "../cloudflare/r2-rest-state-store";
|
|
7
7
|
import { Scope } from "../scope";
|
|
8
8
|
import type { StateStoreType } from "../state";
|
|
9
9
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import * as esbuild from "esbuild";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Runs only tests that have changed dependencies
|
|
12
|
+
* @param directory Directory to scan for test files
|
|
13
|
+
* @param baseCommit Optional base commit to compare against
|
|
14
|
+
*/
|
|
15
|
+
export async function runChangedTests(
|
|
16
|
+
directory: string,
|
|
17
|
+
baseCommit?: string
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
const changedTests = await findChangedTestFiles(directory, baseCommit);
|
|
20
|
+
|
|
21
|
+
if (changedTests.length === 0) {
|
|
22
|
+
console.log("No tests affected by recent changes.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Run the tests with bun using spawn for stdio inheritance
|
|
27
|
+
return new Promise<void>((resolve, reject) => {
|
|
28
|
+
console.log(`bun test ${changedTests.join(" ")}`);
|
|
29
|
+
// resolve();
|
|
30
|
+
const child = spawn("bun", ["test", ...changedTests], { stdio: "inherit" });
|
|
31
|
+
|
|
32
|
+
child.on("close", (code) => {
|
|
33
|
+
if (code === 0) resolve();
|
|
34
|
+
else reject(new Error(`Tests exited with code ${code}`));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
child.on("error", reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Identifies test files that have changed or depend on changed files
|
|
43
|
+
* @param directory Directory to scan for test files
|
|
44
|
+
* @param baseCommit Optional base commit to compare against (defaults to HEAD~1)
|
|
45
|
+
* @returns Array of changed test files
|
|
46
|
+
*/
|
|
47
|
+
export async function findChangedTestFiles(
|
|
48
|
+
directory: string,
|
|
49
|
+
baseCommit: string = "HEAD~1"
|
|
50
|
+
): Promise<string[]> {
|
|
51
|
+
// 1. Find all test files
|
|
52
|
+
const testFiles = await findTestFiles(directory);
|
|
53
|
+
|
|
54
|
+
// 2. Get git changed files
|
|
55
|
+
const changedFiles = await getChangedFiles(baseCommit);
|
|
56
|
+
|
|
57
|
+
// 3. Process each test file
|
|
58
|
+
const changedTestFiles: string[] = [];
|
|
59
|
+
|
|
60
|
+
for (const testFile of testFiles) {
|
|
61
|
+
// Get dependency tree for the test file
|
|
62
|
+
const dependencies = await getDependencies(testFile);
|
|
63
|
+
|
|
64
|
+
// Check if any dependencies have changed
|
|
65
|
+
const changedDependencies = [...dependencies].filter((dep) =>
|
|
66
|
+
changedFiles.has(dep)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (changedDependencies.length > 0) {
|
|
70
|
+
changedTestFiles.push(testFile);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return changedTestFiles.map((p) => path.relative(process.cwd(), p));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Finds all test files in a directory
|
|
79
|
+
* @param directory Directory to scan
|
|
80
|
+
* @returns Array of test file paths
|
|
81
|
+
*/
|
|
82
|
+
async function findTestFiles(directory: string): Promise<string[]> {
|
|
83
|
+
return await glob(path.join(directory, "**/*.test.ts"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets list of files changed since a base commit
|
|
88
|
+
* @param baseCommit Base commit to compare against
|
|
89
|
+
* @returns Set of changed file paths
|
|
90
|
+
*/
|
|
91
|
+
async function getChangedFiles(baseCommit: string): Promise<Set<string>> {
|
|
92
|
+
// Get changed files from git
|
|
93
|
+
const { stdout: gitOutput } = await execAsync(
|
|
94
|
+
`git diff --name-only ${baseCommit}`
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Convert to set of absolute paths
|
|
98
|
+
const changedFiles = new Set<string>();
|
|
99
|
+
const { stdout: workDir } = await execAsync("git rev-parse --show-toplevel");
|
|
100
|
+
const rootDir = workDir.trim();
|
|
101
|
+
|
|
102
|
+
gitOutput
|
|
103
|
+
.split("\n")
|
|
104
|
+
.filter((line) => line.trim().length > 0)
|
|
105
|
+
.forEach((file) => {
|
|
106
|
+
changedFiles.add(path.resolve(rootDir, file));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return changedFiles;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Gets dependency tree for a test file
|
|
114
|
+
* @param testFile Path to test file
|
|
115
|
+
* @returns Set of dependency file paths
|
|
116
|
+
*/
|
|
117
|
+
async function getDependencies(testFile: string): Promise<Set<string>> {
|
|
118
|
+
const dependencies = new Set<string>();
|
|
119
|
+
|
|
120
|
+
// Add the test file itself to dependencies
|
|
121
|
+
const absTestPath = path.resolve(testFile);
|
|
122
|
+
dependencies.add(absTestPath);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Use esbuild with metafile to get dependencies
|
|
126
|
+
const result = await esbuild.build({
|
|
127
|
+
entryPoints: [testFile],
|
|
128
|
+
write: false,
|
|
129
|
+
platform: "node",
|
|
130
|
+
format: "esm",
|
|
131
|
+
bundle: true,
|
|
132
|
+
metafile: true,
|
|
133
|
+
external: ["@cloudflare/workers-types", "bun:test"],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!result.metafile) {
|
|
137
|
+
return dependencies;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Function to normalize paths for comparison
|
|
141
|
+
const normalizePath = (p: string) => path.resolve(p).replace(/\\/g, "/");
|
|
142
|
+
|
|
143
|
+
// Process imports from the metafile
|
|
144
|
+
const { inputs } = result.metafile;
|
|
145
|
+
for (const filePath in inputs) {
|
|
146
|
+
// Skip node_modules and non-local imports
|
|
147
|
+
if (
|
|
148
|
+
filePath.includes("node_modules") ||
|
|
149
|
+
filePath.includes("bun:") ||
|
|
150
|
+
!filePath.endsWith(".ts")
|
|
151
|
+
) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const absolutePath = normalizePath(filePath);
|
|
156
|
+
dependencies.add(absolutePath);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return dependencies;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(`Error analyzing dependencies for ${testFile}:`, error);
|
|
162
|
+
// Return at least the test file itself
|
|
163
|
+
return dependencies;
|
|
164
|
+
}
|
|
165
|
+
}
|
package/src/util/content-type.ts
CHANGED
package/src/util/rm.ts
CHANGED
package/src/web/astro.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { exec } from "child_process";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
import { promisify } from "util";
|
|
5
5
|
import type { Context } from "../context";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
StaticTypeScriptFile,
|
|
11
|
-
} from "../fs";
|
|
6
|
+
import { Folder } from "../fs/folder";
|
|
7
|
+
import { StaticJsonFile } from "../fs/static-json-file";
|
|
8
|
+
import { StaticTextFile } from "../fs/static-text-file";
|
|
9
|
+
import { StaticTypeScriptFile } from "../fs/static-typescript-file";
|
|
12
10
|
import { Resource } from "../resource";
|
|
13
11
|
import { ShadcnUI } from "./shadcn";
|
|
14
12
|
|
|
@@ -198,7 +196,7 @@ export const AstroProject = Resource(
|
|
|
198
196
|
async function (
|
|
199
197
|
this: Context<AstroProject>,
|
|
200
198
|
id: string,
|
|
201
|
-
props: AstroProjectProps
|
|
199
|
+
props: AstroProjectProps
|
|
202
200
|
): Promise<AstroProject> {
|
|
203
201
|
const dir = props.dir ?? props.name;
|
|
204
202
|
|
|
@@ -396,7 +394,7 @@ export const AstroProject = Resource(
|
|
|
396
394
|
export default defineConfig({${integrationsStr}${vitePluginsStr}
|
|
397
395
|
site: "https://example.com",
|
|
398
396
|
output: "static"
|
|
399
|
-
})
|
|
397
|
+
});`
|
|
400
398
|
);
|
|
401
399
|
}
|
|
402
400
|
|
|
@@ -427,14 +425,14 @@ pnpm-debug.log*
|
|
|
427
425
|
# macOS-specific files
|
|
428
426
|
.DS_Store
|
|
429
427
|
.astro/
|
|
430
|
-
|
|
428
|
+
`
|
|
431
429
|
);
|
|
432
430
|
|
|
433
431
|
// Create .env.d.ts
|
|
434
432
|
await StaticTextFile(
|
|
435
433
|
path.join(dir, "src", "env.d.ts"),
|
|
436
434
|
`/// <reference types="astro/client" />
|
|
437
|
-
|
|
435
|
+
`
|
|
438
436
|
);
|
|
439
437
|
|
|
440
438
|
// Create a basic layout
|
|
@@ -478,7 +476,7 @@ const { title, description = "Welcome to my Astro site" } = Astro.props;
|
|
|
478
476
|
Bitstream Vera Sans Mono, Courier New, monospace;
|
|
479
477
|
}
|
|
480
478
|
</style>
|
|
481
|
-
|
|
479
|
+
`
|
|
482
480
|
);
|
|
483
481
|
|
|
484
482
|
// Create a basic index page
|
|
@@ -532,7 +530,7 @@ import Layout from '../layouts/Layout.astro';
|
|
|
532
530
|
padding: 0.3em 0.45em;
|
|
533
531
|
}
|
|
534
532
|
</style>
|
|
535
|
-
|
|
533
|
+
`
|
|
536
534
|
);
|
|
537
535
|
|
|
538
536
|
// Create public/favicon.svg
|
|
@@ -548,7 +546,7 @@ import Layout from '../layouts/Layout.astro';
|
|
|
548
546
|
</linearGradient>
|
|
549
547
|
</defs>
|
|
550
548
|
</svg>
|
|
551
|
-
|
|
549
|
+
`
|
|
552
550
|
);
|
|
553
551
|
}
|
|
554
552
|
|
|
@@ -590,7 +588,7 @@ import Layout from '../layouts/Layout.astro';
|
|
|
590
588
|
`@tailwind base;
|
|
591
589
|
@tailwind components;
|
|
592
590
|
@tailwind utilities;
|
|
593
|
-
|
|
591
|
+
`
|
|
594
592
|
);
|
|
595
593
|
break;
|
|
596
594
|
case "mdx":
|
|
@@ -640,5 +638,5 @@ import Layout from '../layouts/Layout.astro';
|
|
|
640
638
|
await exec(`bun add ${depsArg}`);
|
|
641
639
|
}
|
|
642
640
|
}
|
|
643
|
-
}
|
|
641
|
+
}
|
|
644
642
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exec } from "child_process";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
4
|
import { promisify } from "util";
|
|
5
5
|
import type { Context } from "../context";
|
|
6
6
|
import { Resource } from "../resource";
|
|
@@ -44,13 +44,13 @@ export const ShadcnComponent = Resource(
|
|
|
44
44
|
async function (
|
|
45
45
|
this: Context<ShadcnComponent>,
|
|
46
46
|
id: string,
|
|
47
|
-
props: ShadcnComponentProps
|
|
47
|
+
props: ShadcnComponentProps
|
|
48
48
|
): Promise<ShadcnComponent> {
|
|
49
49
|
if (this.phase === "delete") {
|
|
50
50
|
await ignore("ENOENT", () =>
|
|
51
51
|
fs.unlink(
|
|
52
|
-
path.join(props.cwd, "src", "components", "ui", `${props.name}.tsx`)
|
|
53
|
-
)
|
|
52
|
+
path.join(props.cwd, "src", "components", "ui", `${props.name}.tsx`)
|
|
53
|
+
)
|
|
54
54
|
);
|
|
55
55
|
return this.destroy();
|
|
56
56
|
}
|
|
@@ -68,5 +68,5 @@ export const ShadcnComponent = Resource(
|
|
|
68
68
|
await execAsync(addCommand, { cwd: props.cwd });
|
|
69
69
|
|
|
70
70
|
return this(props);
|
|
71
|
-
}
|
|
71
|
+
}
|
|
72
72
|
);
|
package/src/web/shadcn.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { exec } from "child_process";
|
|
2
|
-
import path from "path";
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { promisify } from "util";
|
|
4
4
|
import type { Context } from "../context";
|
|
5
|
-
import { Folder
|
|
5
|
+
import { Folder } from "../fs/folder";
|
|
6
|
+
import { StaticJsonFile } from "../fs/static-json-file";
|
|
6
7
|
import { StaticTypeScriptFile } from "../fs/static-typescript-file";
|
|
7
8
|
import { Resource } from "../resource";
|
|
8
9
|
import { ShadcnComponent } from "./shadcn-component";
|
|
@@ -120,14 +121,14 @@ export const ShadcnUI = Resource(
|
|
|
120
121
|
async function (
|
|
121
122
|
this: Context<ShadcnUI>,
|
|
122
123
|
id: string,
|
|
123
|
-
props: ShadcnUIProps
|
|
124
|
+
props: ShadcnUIProps
|
|
124
125
|
): Promise<ShadcnUI> {
|
|
125
126
|
if (this.phase === "delete") {
|
|
126
127
|
// For a delete phase, we don't perform any action
|
|
127
128
|
// as removing Shadcn UI would require removing many files
|
|
128
129
|
// which could be destructive to the project
|
|
129
130
|
console.log(
|
|
130
|
-
"Note: ShadcnUI delete phase does not remove installed components"
|
|
131
|
+
"Note: ShadcnUI delete phase does not remove installed components"
|
|
131
132
|
);
|
|
132
133
|
|
|
133
134
|
return this.destroy();
|
|
@@ -171,15 +172,15 @@ export const ShadcnUI = Resource(
|
|
|
171
172
|
props.cwd,
|
|
172
173
|
props.srcDir !== false ? "src" : "",
|
|
173
174
|
"components",
|
|
174
|
-
"ui"
|
|
175
|
-
)
|
|
175
|
+
"ui"
|
|
176
|
+
)
|
|
176
177
|
);
|
|
177
178
|
|
|
178
179
|
// Create lib directory
|
|
179
180
|
const libPath = path.join(
|
|
180
181
|
props.cwd,
|
|
181
182
|
props.srcDir !== false ? "src" : "",
|
|
182
|
-
"lib"
|
|
183
|
+
"lib"
|
|
183
184
|
);
|
|
184
185
|
const lib = await Folder(libPath);
|
|
185
186
|
|
|
@@ -215,5 +216,5 @@ export const ShadcnUI = Resource(
|
|
|
215
216
|
ui,
|
|
216
217
|
lib,
|
|
217
218
|
});
|
|
218
|
-
}
|
|
219
|
+
}
|
|
219
220
|
);
|