Skip to content

Commit ab64df7

Browse files
committed
added basic generator to show loaded files
1 parent 115fe78 commit ab64df7

5 files changed

Lines changed: 105 additions & 61 deletions

File tree

src/cli/commands/site/deploy.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,40 @@ import { deploySite } from "@core/site/index.js";
66
import { runCommand, runTask } from "../../utils/index.js";
77

88
async function deployAction(): Promise<void> {
9-
// 1. Load project config
109
const { project } = await readProjectConfig();
1110

12-
// 2. Validate site configuration exists
1311
if (!project.site?.outputDirectory) {
14-
log.error(
15-
"No site configuration found. Please add a 'site.outputDirectory' to your config.jsonc"
12+
throw new Error(
13+
"No site configuration found. Please add 'site.outputDirectory' to your config.jsonc"
1614
);
17-
process.exit(1);
1815
}
1916

2017
const outputDir = resolve(project.root, project.site.outputDirectory);
2118

22-
// 3. Confirm with user
2319
const shouldDeploy = await confirm({
2420
message: `Deploy site from ${project.site.outputDirectory}?`,
2521
});
2622

2723
if (isCancel(shouldDeploy) || !shouldDeploy) {
2824
log.warn("Deployment cancelled");
29-
process.exit(0);
25+
return;
3026
}
3127

32-
// 4. Deploy to Base44
3328
const result = await runTask(
34-
"Deploying site...",
35-
async () => {
36-
return await deploySite(outputDir);
29+
"Reading site files...",
30+
async (updateMessage) => {
31+
return await deploySite(outputDir, (progress) => {
32+
updateMessage(
33+
`Reading files (${progress.current}/${progress.total}): ${progress.path}`
34+
);
35+
});
3736
},
3837
{
3938
successMessage: "Site deployed successfully",
40-
errorMessage: "Failed to deploy site",
39+
errorMessage: "Deployment failed",
4140
}
4241
);
4342

44-
// 5. Display the deployed URL
4543
log.success(`Site deployed to: ${result.url}`);
4644
}
4745

src/cli/utils/runTask.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { spinner } from "@clack/prompts";
55
* The spinner is automatically started, and stopped on both success and error.
66
*
77
* @param startMessage - Message to show when spinner starts
8-
* @param operation - The async operation to execute
8+
* @param operation - The async operation to execute. Receives an updateMessage function
9+
* to update the spinner text during long-running operations.
910
* @param options - Optional configuration for success/error messages
1011
* @returns The result of the operation
1112
*
1213
* @example
14+
* // Simple usage
1315
* const data = await runTask(
1416
* "Fetching data...",
1517
* async () => {
@@ -21,10 +23,24 @@ import { spinner } from "@clack/prompts";
2123
* errorMessage: "Failed to fetch data",
2224
* }
2325
* );
26+
*
27+
* @example
28+
* // With progress updates
29+
* const result = await runTask(
30+
* "Processing files...",
31+
* async (updateMessage) => {
32+
* for (const file of files) {
33+
* updateMessage(`Processing ${file.name}...`);
34+
* await process(file);
35+
* }
36+
* return files.length;
37+
* },
38+
* { successMessage: "All files processed" }
39+
* );
2440
*/
2541
export async function runTask<T>(
2642
startMessage: string,
27-
operation: () => Promise<T>,
43+
operation: (updateMessage: (message: string) => void) => Promise<T>,
2844
options?: {
2945
successMessage?: string;
3046
errorMessage?: string;
@@ -33,8 +49,10 @@ export async function runTask<T>(
3349
const s = spinner();
3450
s.start(startMessage);
3551

52+
const updateMessage = (message: string) => s.message(message);
53+
3654
try {
37-
const result = await operation();
55+
const result = await operation(updateMessage);
3856
s.stop(options?.successMessage || startMessage);
3957
return result;
4058
} catch (error) {

src/core/site/api.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,17 @@ import type { SiteFile, DeployResponse } from "./schema.js";
55
*
66
* @param files - Array of files with base64-encoded content to upload
77
* @returns Deploy response with the site URL
8-
*
9-
* @example
10-
* const files = await readSiteFiles("./dist");
11-
* const { url } = await uploadSite(files);
128
*/
139
export async function uploadSite(files: SiteFile[]): Promise<DeployResponse> {
1410
// TODO: Implement actual FormData upload to Base44 API
1511
// The endpoint will accept multipart/form-data with all files
1612
// and return the deployed site URL
1713

1814
// Placeholder implementation - simulate API call
19-
await Promise.resolve();
15+
await new Promise((resolve) => setTimeout(resolve, 2000));
2016

2117
// Log file count for debugging (remove when implementing real API)
22-
console.log(`[Placeholder] Would upload ${files.length} files`);
18+
// console.log(`[Placeholder] Would upload ${files.length} files`);
2319

2420
return {
2521
url: "https://example.base44.app",

src/core/site/config.ts

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,49 @@ import { join } from "node:path";
33
import { globby } from "globby";
44
import type { SiteFile } from "./schema.js";
55

6-
/**
7-
* Reads all files from a site output directory and encodes them as base64.
8-
*
9-
* @param outputDir - The directory containing built site files (e.g., "dist")
10-
* @returns Array of SiteFile objects with relative paths and base64 content
11-
*
12-
* @example
13-
* const files = await readSiteFiles("./dist");
14-
* // files = [{ path: "index.html", content: "PGh0bWw+..." }, ...]
15-
*/
16-
export async function readSiteFiles(outputDir: string): Promise<SiteFile[]> {
17-
// Glob all files (not directories) in the output directory
18-
const filePaths = await globby("**/*", {
6+
async function readSiteFile(
7+
outputDir: string,
8+
relativePath: string
9+
): Promise<SiteFile> {
10+
const absolutePath = join(outputDir, relativePath);
11+
const buffer = await readFile(absolutePath);
12+
const content = buffer.toString("base64");
13+
14+
await new Promise((resolve) => setTimeout(resolve, 20));
15+
16+
return {
17+
path: relativePath,
18+
content,
19+
};
20+
}
21+
22+
export async function getSiteFilePaths(outputDir: string): Promise<string[]> {
23+
return await globby("**/*", {
1924
cwd: outputDir,
2025
onlyFiles: true,
2126
absolute: false,
2227
});
28+
}
2329

24-
if (filePaths.length === 0) {
25-
return [];
30+
/**
31+
* Reads site files one by one, yielding each file as it's read.
32+
* Useful for showing progress during file reading.
33+
*
34+
* @param outputDir - The directory containing built site files
35+
* @param filePaths - Array of relative file paths to read
36+
* @yields Each file as it's read
37+
*
38+
* @example
39+
* const paths = await getSiteFilePaths("./dist");
40+
* for await (const file of readSiteFilesStream("./dist", paths)) {
41+
* console.log(`Read: ${file.path}`);
42+
* }
43+
*/
44+
export async function* readSiteFilesStream(
45+
outputDir: string,
46+
filePaths: string[]
47+
): AsyncGenerator<SiteFile> {
48+
for (const relativePath of filePaths) {
49+
yield await readSiteFile(outputDir, relativePath);
2650
}
27-
28-
// Read each file and encode as base64
29-
const files = await Promise.all(
30-
filePaths.map(async (relativePath): Promise<SiteFile> => {
31-
const absolutePath = join(outputDir, relativePath);
32-
const buffer = await readFile(absolutePath);
33-
const content = buffer.toString("base64");
34-
35-
return {
36-
path: relativePath,
37-
content,
38-
};
39-
})
40-
);
41-
42-
return files;
4351
}

src/core/site/deploy.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
1-
import type { DeployResponse } from "./schema.js";
2-
import { readSiteFiles } from "./config.js";
1+
import type { SiteFile, DeployResponse } from "./schema.js";
2+
import { getSiteFilePaths, readSiteFilesStream } from "./config.js";
33
import { uploadSite } from "./api.js";
44

5+
export interface DeploySiteProgress {
6+
total: number;
7+
current: number;
8+
path: string;
9+
}
10+
511
/**
612
* Deploys a site from the given output directory to Base44 hosting.
7-
* Reads all files, validates, and uploads to the API.
13+
* Reads files one by one with progress callback, validates, and uploads to the API.
814
*
915
* @param outputDir - The directory containing built site files (e.g., "./dist")
16+
* @param onProgress - Optional callback called for each file read
1017
* @returns Deploy response with the site URL
1118
* @throws Error if no files found in the output directory
1219
*
1320
* @example
14-
* const { url } = await deploySite("./dist");
15-
* console.log(`Deployed to: ${url}`);
21+
* const { url } = await deploySite("./dist", (progress) => {
22+
* console.log(`Reading ${progress.current}/${progress.total}: ${progress.path}`);
23+
* });
1624
*/
17-
export async function deploySite(outputDir: string): Promise<DeployResponse> {
18-
const files = await readSiteFiles(outputDir);
25+
export async function deploySite(
26+
outputDir: string,
27+
onProgress?: (progress: DeploySiteProgress) => void
28+
): Promise<DeployResponse> {
29+
const filePaths = await getSiteFilePaths(outputDir);
1930

20-
if (files.length === 0) {
31+
if (filePaths.length === 0) {
2132
throw new Error(
2233
`No files found in output directory: ${outputDir}. Make sure to build your project first.`
2334
);
2435
}
2536

37+
const files: SiteFile[] = [];
38+
let current = 0;
39+
40+
for await (const file of readSiteFilesStream(outputDir, filePaths)) {
41+
current++;
42+
onProgress?.({
43+
total: filePaths.length,
44+
current,
45+
path: file.path,
46+
});
47+
files.push(file);
48+
}
49+
2650
return await uploadSite(files);
2751
}

0 commit comments

Comments
 (0)