From 273fd929e0e8ad4da990cc6fd654852ce22c5352 Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sat, 25 Jan 2025 20:05:52 +0530 Subject: [PATCH 1/7] Add support for configuring new Astro apps --- scripts/add-astro.mjs | 260 +++++++++++++++++++++ utils/logger.js | 24 ++ utils/spinner.js | 52 +++++ utils/templates/astro/astroConfig.js | 21 ++ utils/templates/workflow/buildArtifacts.js | 8 + utils/templates/workflow/pagesWorkflow.js | 41 ++++ 6 files changed, 406 insertions(+) create mode 100644 scripts/add-astro.mjs create mode 100644 utils/logger.js create mode 100644 utils/spinner.js create mode 100644 utils/templates/astro/astroConfig.js create mode 100644 utils/templates/workflow/buildArtifacts.js create mode 100644 utils/templates/workflow/pagesWorkflow.js diff --git a/scripts/add-astro.mjs b/scripts/add-astro.mjs new file mode 100644 index 0000000..e046b68 --- /dev/null +++ b/scripts/add-astro.mjs @@ -0,0 +1,260 @@ +import { spawn } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import readline from 'node:readline'; +import yaml from 'js-yaml'; +import { updatedConfig } from '../utils/templates/astro/astroConfig.js'; +import { pagesWorkflow } from '../utils/templates/workflow/pagesWorkflow.js'; +import { buildArtifacts } from '../utils/templates/workflow/buildArtifacts.js'; +import Logger from '../utils/logger.js'; +import Spinner from '../utils/spinner.js'; +/** + * Get the directory of the newly created project. + * @param {string} appDir - The root directory where the project is created. + * @returns {Promise} - The full path of the most recently created project directory. + * @throws {Error} If no project directories are found. + */ +async function getCreatedProjectDir(appDir) { + const projectRoot = path.join(appDir); + const directories = fs + .readdirSync(projectRoot) + .filter((subdir) => + fs.lstatSync(path.join(projectRoot, subdir)).isDirectory(), + ); + + if (directories.length === 0) { + throw new Error('No project directories found in the specified location.'); + } + + const sortedDirectories = directories + .map((dir) => ({ + name: dir, + modifiedTime: fs.statSync(path.join(projectRoot, dir)).mtime.getTime(), + })) + .sort((a, b) => b.modifiedTime - a.modifiedTime); + + return path.join(projectRoot, sortedDirectories[0].name); +} + +/** + * Update the Astro configuration file with Cloudflare integration. + * @param {string} projectDir - The directory of the Astro project. + * @throws {Error} If failed to update Astro configuration. + */ +function updateAstroConfig(projectDir) { + const configFilePath = path.join(projectDir, 'astro.config.mjs'); + const configContent = ` +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare';${updatedConfig} +`; + try { + fs.writeFileSync(configFilePath, configContent, 'utf-8'); + Logger.info('Configuration updated successfully!'); + return true; + } catch (error) { + Logger.error(`Failed to update Astro configuration: ${error.message}`); + throw error; + } +} + +/** + * Update the `preview` script in the `package.json` file. + * @param {string} projectDir - The directory of the Astro project. + * @throws {Error} If failed to update `package.json` file. + */ +function updatePreviewScript(projectDir) { + const packageJsonPath = path.join(projectDir, 'package.json'); + + const previewCommand = + 'wrangler pages dev --persist-to=../../.wrangler/.test/state'; + + try { + const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8'); + const packageJson = JSON.parse(packageJsonContent); + + packageJson.scripts = packageJson.scripts || {}; + packageJson.scripts.preview = previewCommand; + + fs.writeFileSync( + packageJsonPath, + JSON.stringify(packageJson, null, 2), + 'utf-8', + ); + Logger.info('Preview script updated successfully!'); + return true; + } catch (error) { + Logger.error(`Failed to update package.json: ${error.message}`); + throw error; + } +} + +/** + * Update the GitHub Actions workflow for deploying the project. + * @param {string} rootDir - The root directory of the project. + * @param {string} projectDir - The directory of the Astro project. + * @throws {Error} If failed to update workflow script. + */ +function updateWorkflowScript(rootDir, projectDir) { + const workflowPath = path.join(rootDir, '.github', 'workflows', 'deploy.yml'); + const projectName = path.basename(projectDir); + const workflowContent = pagesWorkflow(projectName); + const buildSteps = buildArtifacts(projectName); + + try { + const workflowContentRaw = fs.readFileSync(workflowPath, 'utf-8'); + const existingWorkflow = yaml.load(workflowContentRaw); + + const deployJobName = `deploy_${projectName}`; + + existingWorkflow.jobs[deployJobName] = workflowContent; + + if (existingWorkflow.jobs.build) { + existingWorkflow.jobs.build.steps.push(buildSteps); + } else { + existingWorkflow.jobs.build = { steps: [buildSteps] }; + } + + fs.writeFileSync(workflowPath, yaml.dump(existingWorkflow), 'utf-8'); + Logger.info('Workflow script updated successfully!'); + } catch (error) { + Logger.error(`Failed to update workflow script: ${error.message}`); + throw error; + } +} + +const installWranglerPkg = (projectDir, spinner) => { + return new Promise((resolve, reject) => { + try { + spinner.start('🌤️ Installing wrangler...'); + + const addWranglerProcess = spawn( + `npm`, + ['add', 'wrangler', '--save-dev'], + { + cwd: projectDir, + stdio: 'pipe', + shell: true, + env: { ...process.env }, + }, + ); + + addWranglerProcess.stderr.on('data', (data) => { + spinner.stop(); + Logger.warn(`Unexpected output: ${data.toString()}`); + }); + + addWranglerProcess.on('error', (error) => { + spinner.stop(); + Logger.error(`Process spawn error: ${error.message}`); + reject(error); + }); + + addWranglerProcess.on('exit', (code) => { + spinner.stop(); + if (code === 0) { + Logger.info('Wrangler installed successfully!'); + resolve(); + } else { + reject(new Error(`Wrangler installation exited with code ${code}`)); + } + }); + } catch (error) { + spinner.stop(); + Logger.error(`Failed to install wrangler: ${error.message}`); + reject(error); + } + }); +}; + +/** + * Create a new Astro project with flarekit configurations. + * @param {string} rootDir - The root directory for the project. + * @throws {Error} If failed to create project. + */ +export async function addAstroProject(rootDir) { + const spinner = new Spinner(); + const appDir = path.join(rootDir, 'apps'); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + return new Promise((resolve, reject) => { + rl.question('\n📝 Enter the project name: ', (inputName) => { + const projectName = inputName.trim().replace(/\s+/g, '-').toLowerCase(); + + const astroProcess = spawn( + 'npm', + [ + 'create', + 'astro@latest', + projectName, + '--', + '--template', + 'basics', + '--no-git', + '--install', + '--add', + 'cloudflare', + '--yes', + ], + { + cwd: appDir, + stdio: 'pipe', + shell: true, + env: { ...process.env }, + }, + ); + process.stdout.write('\n'); + astroProcess.stdout.on('data', () => { + spinner.start('🔥 Creating your Astro app...'); + }); + + astroProcess.stderr.on('data', (data) => { + spinner.stop(); + Logger.warn(`Unexpected output: ${data.toString()}`); + }); + + astroProcess.on('error', (error) => { + spinner.stop(); + rl.close(); + Logger.error(`Process spawn error: ${error.message}`); + reject(error); + }); + + astroProcess.on('exit', async (code) => { + spinner.stop(); + rl.close(); + + if (code === 0) { + try { + const projectDir = await getCreatedProjectDir(appDir); + updateAstroConfig(projectDir); + await installWranglerPkg(projectDir, spinner); + updatePreviewScript(projectDir); + updateWorkflowScript(rootDir, projectDir); + + Logger.success( + 'Astro project created and configured successfully!', + ); + spinner.stop(); + resolve(projectDir); + } catch (configError) { + Logger.error( + `Project configuration failed: ${configError.message}`, + ); + reject(configError); + } + } else { + Logger.error('Astro project creation failed.'); + reject( + new Error( + 'Astro project creation process exited with non-zero status', + ), + ); + } + }); + }); + }); +} diff --git a/utils/logger.js b/utils/logger.js new file mode 100644 index 0000000..aa5fb9b --- /dev/null +++ b/utils/logger.js @@ -0,0 +1,24 @@ +class Logger { + x; + static info(message) { + console.log(`\x1b[36m✔️ ${message}\x1b[0m`); + } + + static success(message) { + console.log(`\n\x1b[32m🚀 ${message}\x1b[0m`); + } + + static error(message) { + console.error(`\x1b[31m❌ ${message}\x1b[0m`); + } + + static warn(message) { + console.warn(`\x1b[33m⚠️ ${message}\x1b[0m`); + } + + static debug(message) { + console.log(`\x1b[32m🔍 ${message}\x1b[0m`); + } +} + +export default Logger; diff --git a/utils/spinner.js b/utils/spinner.js new file mode 100644 index 0000000..cbba7fd --- /dev/null +++ b/utils/spinner.js @@ -0,0 +1,52 @@ +class Spinner { + /** + * Create a new spinner instance + */ + constructor() { + this.spinnerChars = ['-', '\\', '|', '/']; + this.spinnerIndex = 0; + this.interval = null; + this.isRunning = false; + } + + /** + * Start the spinner animation + * @param {string} [message='Processing'] - The message to display with the spinner + */ + start(message = 'Processing') { + this.message = message; + if (this.isRunning) return; + this.isRunning = true; + process.stdout.write('\x1B[?25l'); + this.interval = setInterval(() => { + if (!this.isRunning) { + this.stop(); + return; + } + process.stdout.write( + `\r${this.spinnerChars[this.spinnerIndex]} ${this.message}`, + ); + this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerChars.length; + }, 100); + } + + /** + * Stop the spinner and clear the line + */ + stop() { + if (!this.isRunning) return; + + this.isRunning = false; + + if (this.interval) { + clearInterval(this.interval); + this.interval = null; + } + + // Clear the entire line + process.stdout.write('\r' + ' '.repeat(process.stdout.columns) + '\r'); + process.stdout.write('\x1B[?25h'); + } +} + +export default Spinner; diff --git a/utils/templates/astro/astroConfig.js b/utils/templates/astro/astroConfig.js new file mode 100644 index 0000000..b4cc626 --- /dev/null +++ b/utils/templates/astro/astroConfig.js @@ -0,0 +1,21 @@ +export const updatedConfig = ` +export default defineConfig({ + output: 'server', + adapter: cloudflare({ + platformProxy: { + enabled: true, + persist: { + path: '../../.wrangler/state/v3', + }, + }, + }), + security: { + checkOrigin: false, + }, + vite: { + optimizeDeps: { + include: ['@flarekit/database'], + }, + }, +}); +`; diff --git a/utils/templates/workflow/buildArtifacts.js b/utils/templates/workflow/buildArtifacts.js new file mode 100644 index 0000000..ce4598a --- /dev/null +++ b/utils/templates/workflow/buildArtifacts.js @@ -0,0 +1,8 @@ +export const buildArtifacts = (appName) => ({ + name: `Archive ${appName} artifacts`, + uses: 'actions/upload-artifact@v4', + with: { + name: `${appName}-dist`, + path: `./apps/${appName}/dist`, + }, +}); diff --git a/utils/templates/workflow/pagesWorkflow.js b/utils/templates/workflow/pagesWorkflow.js new file mode 100644 index 0000000..3d0dfce --- /dev/null +++ b/utils/templates/workflow/pagesWorkflow.js @@ -0,0 +1,41 @@ +export const pagesWorkflow = (appName) => ({ + 'runs-on': 'ubuntu-24.04', + needs: ['migrate'], + steps: [ + { name: 'Check out code', uses: 'actions/checkout@v4' }, + { + name: 'Use Node', + uses: 'actions/setup-node@v4', + with: { 'node-version': 22 }, + }, + { + name: 'Cache node_modules', + uses: 'actions/cache@v3', + with: { + path: '**/node_modules', + key: "${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}", + 'restore-keys': '${{ runner.os }}-node-', + }, + }, + { name: 'Install dependencies', run: 'npm install' }, + { name: 'Setup Wrangler', run: 'npm run setup' }, + { + name: 'Download build artifacts', + uses: 'actions/download-artifact@v4', + with: { + name: `${appName}-dist`, + path: `./apps/${appName}/dist`, + }, + }, + { + name: `Deploy to @flarekit/${appName}`, + uses: 'cloudflare/wrangler-action@v3', + with: { + accountId: '${{ secrets.CLOUDFLARE_ACCOUNT_ID }}', + apiToken: '${{ secrets.CLOUDFLARE_API_TOKEN }}', + workingDirectory: `./apps/${appName}`, + command: 'pages deploy ./dist', + }, + }, + ], +}); From 43018825cfbaf51780f8ee15a7af34bbf6505260 Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sat, 25 Jan 2025 20:18:07 +0530 Subject: [PATCH 2/7] Refactor argument parsing and enable custom arguments --- scripts/flarekit.mjs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/scripts/flarekit.mjs b/scripts/flarekit.mjs index b91e3ef..eb52f9f 100755 --- a/scripts/flarekit.mjs +++ b/scripts/flarekit.mjs @@ -3,15 +3,43 @@ import { dirname, resolve } from 'node:path'; import { spawn } from 'node:child_process'; import { init } from './setup-wrangler.mjs'; import { fileURLToPath } from 'node:url'; +import { addAstroProject } from './add-astro.mjs'; const rootDir = resolve(dirname(dirname(fileURLToPath(import.meta.url)))); +// Custom commands +const commands = { + add: { + astro: async () => { + addAstroProject(rootDir); + return true; + }, + default: (project) => { + console.log(`Unknown project ${project}...`); + }, + }, + default: (command) => { + console.log(`Unknown command ${command}...`); + }, +}; async function main() { // 1. Run setup await init(); + const [, , command = 'default', ...args] = process.argv; + // Check for custom command + if (commands[command]) { + const result = await ( + commands[command][args[0]] || commands[command].default + )(args[0]); + + if (result) { + return; + } + } + + // If no continuation with default turbo command // 2. Build the turbo command + arguments - const [, , ...args] = process.argv; const turboArgs = args; // e.g. ['run', 'preview'] or whatever you pass console.log(`Executing turbo with args: ${turboArgs.join(' ')}`); From 19fe8cdef071bdcd32ecc9e9db6c9ceb29430861 Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sat, 25 Jan 2025 22:10:03 +0530 Subject: [PATCH 3/7] Fix default commands formation issue --- scripts/flarekit.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flarekit.mjs b/scripts/flarekit.mjs index eb52f9f..fa186d1 100755 --- a/scripts/flarekit.mjs +++ b/scripts/flarekit.mjs @@ -40,7 +40,7 @@ async function main() { // If no continuation with default turbo command // 2. Build the turbo command + arguments - const turboArgs = args; // e.g. ['run', 'preview'] or whatever you pass + const turboArgs = [command, args]; // e.g. ['run', 'preview'] or whatever you pass console.log(`Executing turbo with args: ${turboArgs.join(' ')}`); // 3. Spawn Turbo From 7c5bc186e40de22fcbc27dd31c21e4b903945334 Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sat, 25 Jan 2025 22:40:00 +0530 Subject: [PATCH 4/7] Configure wrangler.config for new Astro project --- scripts/add-astro.mjs | 31 +++++++++++++++++++++++++++++++ utils/templates/wranglerConfig.js | 5 +++++ 2 files changed, 36 insertions(+) create mode 100644 utils/templates/wranglerConfig.js diff --git a/scripts/add-astro.mjs b/scripts/add-astro.mjs index e046b68..5a17048 100644 --- a/scripts/add-astro.mjs +++ b/scripts/add-astro.mjs @@ -6,6 +6,7 @@ import yaml from 'js-yaml'; import { updatedConfig } from '../utils/templates/astro/astroConfig.js'; import { pagesWorkflow } from '../utils/templates/workflow/pagesWorkflow.js'; import { buildArtifacts } from '../utils/templates/workflow/buildArtifacts.js'; +import { wranglerConfig } from '../utils/templates/wranglerConfig.js'; import Logger from '../utils/logger.js'; import Spinner from '../utils/spinner.js'; /** @@ -122,6 +123,12 @@ function updateWorkflowScript(rootDir, projectDir) { } } +/** + * Install the `wrangler` package in the project. + * @param {string} rootDir - The root directory of the project. + * @param {Spinner} spinner - The spinner instance. + * @throws {Error} If failed to install wrangler. + */ const installWranglerPkg = (projectDir, spinner) => { return new Promise((resolve, reject) => { try { @@ -166,6 +173,29 @@ const installWranglerPkg = (projectDir, spinner) => { }); }; +/** + * Configure wrangler.config.json file to the project. + * @param {string} rootDir - The root directory of the project. + * @throws {Error} If failed to configure wrangler.config.json file. + */ +const addWranflerConfig = (projectDir) => { + const wranglerConfigFilePath = path.join(projectDir, 'wrangler.config.json'); + try { + const projectName = path.basename(projectDir); + const wranglerConfigContent = wranglerConfig(projectName); + + fs.writeFileSync( + wranglerConfigFilePath, + JSON.stringify(wranglerConfigContent, null, 2), + 'utf-8', + ); + Logger.info('Wrangler config added successfully!'); + return true; + } catch (error) { + Logger.error(`Failed to add wrangler.config.json: ${error.message}`); + throw error; + } +}; /** * Create a new Astro project with flarekit configurations. * @param {string} rootDir - The root directory for the project. @@ -232,6 +262,7 @@ export async function addAstroProject(rootDir) { const projectDir = await getCreatedProjectDir(appDir); updateAstroConfig(projectDir); await installWranglerPkg(projectDir, spinner); + addWranflerConfig(projectDir); updatePreviewScript(projectDir); updateWorkflowScript(rootDir, projectDir); diff --git a/utils/templates/wranglerConfig.js b/utils/templates/wranglerConfig.js new file mode 100644 index 0000000..ff83733 --- /dev/null +++ b/utils/templates/wranglerConfig.js @@ -0,0 +1,5 @@ +export const wranglerConfig = (appName) => ({ + $schema: '../../node_modules/wrangler/config-schema.json', + name: `flarekit-${appName}`, + pages_build_output_dir: './dist', +}); From 45bfebdcb76454bca169e75783020ea534303a80 Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sun, 26 Jan 2025 00:48:34 +0530 Subject: [PATCH 5/7] Improve error handling and logging for 'add Astro' command --- scripts/add-astro.mjs | 168 ++++++++++++++++++++---------------------- scripts/flarekit.mjs | 19 ++--- utils/logger.js | 3 +- 3 files changed, 90 insertions(+), 100 deletions(-) diff --git a/scripts/add-astro.mjs b/scripts/add-astro.mjs index 5a17048..397b91a 100644 --- a/scripts/add-astro.mjs +++ b/scripts/add-astro.mjs @@ -9,6 +9,47 @@ import { buildArtifacts } from '../utils/templates/workflow/buildArtifacts.js'; import { wranglerConfig } from '../utils/templates/wranglerConfig.js'; import Logger from '../utils/logger.js'; import Spinner from '../utils/spinner.js'; +/** + * Run the `npm create astro@latest` command to create a new Astro project. + * @param {string} appDir - The directory where the project will be created. + * @param {string} projectName - The name of the new project. + * @returns {Promise} - A promise that resolves when process exits. + * */ +async function runAstroCreate(appDir, projectName) { + return new Promise((resolve, reject) => { + const astroProcess = spawn( + 'npm', + [ + 'create', + 'astro@latest', + projectName, + '--', + '--template', + 'basics', + '--no-git', + '--install', + '--add', + 'cloudflare', + '--yes', + ], + { + cwd: appDir, + stdio: 'pipe', + shell: true, + }, + ); + + astroProcess.on('error', (error) => reject(new Error(error.message))); + astroProcess.on('exit', (code) => { + if (code === 0) resolve(); + else + reject( + new Error(`Astro project creation process failed with code ${code}`), + ); + }); + }); +} + /** * Get the directory of the newly created project. * @param {string} appDir - The root directory where the project is created. @@ -53,8 +94,7 @@ import cloudflare from '@astrojs/cloudflare';${updatedConfig} Logger.info('Configuration updated successfully!'); return true; } catch (error) { - Logger.error(`Failed to update Astro configuration: ${error.message}`); - throw error; + throw new Error(`Failed to update Astro configuration: ${error.message}`); } } @@ -84,8 +124,7 @@ function updatePreviewScript(projectDir) { Logger.info('Preview script updated successfully!'); return true; } catch (error) { - Logger.error(`Failed to update package.json: ${error.message}`); - throw error; + throw new Error(`Failed to update package.json.`); } } @@ -117,9 +156,9 @@ function updateWorkflowScript(rootDir, projectDir) { fs.writeFileSync(workflowPath, yaml.dump(existingWorkflow), 'utf-8'); Logger.info('Workflow script updated successfully!'); + return true; } catch (error) { - Logger.error(`Failed to update workflow script: ${error.message}`); - throw error; + throw new Error(`Failed to update workflow script.`); } } @@ -146,18 +185,14 @@ const installWranglerPkg = (projectDir, spinner) => { ); addWranglerProcess.stderr.on('data', (data) => { - spinner.stop(); Logger.warn(`Unexpected output: ${data.toString()}`); }); addWranglerProcess.on('error', (error) => { - spinner.stop(); - Logger.error(`Process spawn error: ${error.message}`); - reject(error); + reject(new Error(`Process spawn error: ${error.message}`)); }); addWranglerProcess.on('exit', (code) => { - spinner.stop(); if (code === 0) { Logger.info('Wrangler installed successfully!'); resolve(); @@ -167,8 +202,7 @@ const installWranglerPkg = (projectDir, spinner) => { }); } catch (error) { spinner.stop(); - Logger.error(`Failed to install wrangler: ${error.message}`); - reject(error); + reject(new Error(`Failed to install wrangler: ${error.message}`)); } }); }; @@ -192,8 +226,7 @@ const addWranflerConfig = (projectDir) => { Logger.info('Wrangler config added successfully!'); return true; } catch (error) { - Logger.error(`Failed to add wrangler.config.json: ${error.message}`); - throw error; + throw new Error(`Failed to add wrangler.config.json.`); } }; /** @@ -210,82 +243,39 @@ export async function addAstroProject(rootDir) { output: process.stdout, }); - return new Promise((resolve, reject) => { - rl.question('\n📝 Enter the project name: ', (inputName) => { - const projectName = inputName.trim().replace(/\s+/g, '-').toLowerCase(); - - const astroProcess = spawn( - 'npm', - [ - 'create', - 'astro@latest', - projectName, - '--', - '--template', - 'basics', - '--no-git', - '--install', - '--add', - 'cloudflare', - '--yes', - ], - { - cwd: appDir, - stdio: 'pipe', - shell: true, - env: { ...process.env }, - }, - ); - process.stdout.write('\n'); - astroProcess.stdout.on('data', () => { - spinner.start('🔥 Creating your Astro app...'); - }); + const promptUser = (query) => + new Promise((resolve) => + rl.question(query, (prompt) => + resolve(prompt.trim().replace(/\s+/g, '-').toLowerCase()), + ), + ); - astroProcess.stderr.on('data', (data) => { - spinner.stop(); - Logger.warn(`Unexpected output: ${data.toString()}`); - }); + try { + const projectName = await promptUser('\n📝 Enter the project name: '); + rl.close(); - astroProcess.on('error', (error) => { - spinner.stop(); - rl.close(); - Logger.error(`Process spawn error: ${error.message}`); - reject(error); - }); + const projectPath = path.join(appDir, projectName); - astroProcess.on('exit', async (code) => { - spinner.stop(); - rl.close(); + if (fs.existsSync(projectPath)) { + throw new Error(`Project with name "${projectName}" already exists.`); + } - if (code === 0) { - try { - const projectDir = await getCreatedProjectDir(appDir); - updateAstroConfig(projectDir); - await installWranglerPkg(projectDir, spinner); - addWranflerConfig(projectDir); - updatePreviewScript(projectDir); - updateWorkflowScript(rootDir, projectDir); - - Logger.success( - 'Astro project created and configured successfully!', - ); - spinner.stop(); - resolve(projectDir); - } catch (configError) { - Logger.error( - `Project configuration failed: ${configError.message}`, - ); - reject(configError); - } - } else { - Logger.error('Astro project creation failed.'); - reject( - new Error( - 'Astro project creation process exited with non-zero status', - ), - ); - } - }); - }); - }); + spinner.start('🔥 Creating your Astro app...'); + await runAstroCreate(appDir, projectName); + spinner.stop(); + + const projectDir = await getCreatedProjectDir(appDir); + updateAstroConfig(projectDir); + await installWranglerPkg(projectDir, spinner); + addWranflerConfig(projectDir); + updatePreviewScript(projectDir); + updateWorkflowScript(rootDir, projectDir); + + Logger.success('Astro project created and configured successfully!'); + return projectDir; + } catch (error) { + spinner.stop(); + console.error(`\nError: ${error.message}`); + throw error; + } } diff --git a/scripts/flarekit.mjs b/scripts/flarekit.mjs index fa186d1..8760da4 100755 --- a/scripts/flarekit.mjs +++ b/scripts/flarekit.mjs @@ -4,14 +4,20 @@ import { spawn } from 'node:child_process'; import { init } from './setup-wrangler.mjs'; import { fileURLToPath } from 'node:url'; import { addAstroProject } from './add-astro.mjs'; +import Logger from '../utils/logger.js'; const rootDir = resolve(dirname(dirname(fileURLToPath(import.meta.url)))); // Custom commands const commands = { add: { astro: async () => { - addAstroProject(rootDir); - return true; + try { + await addAstroProject(rootDir); + } catch (err) { + Logger.error(`Failed to add Astro project`); + } finally { + return; + } }, default: (project) => { console.log(`Unknown project ${project}...`); @@ -29,13 +35,8 @@ async function main() { const [, , command = 'default', ...args] = process.argv; // Check for custom command if (commands[command]) { - const result = await ( - commands[command][args[0]] || commands[command].default - )(args[0]); - - if (result) { - return; - } + await (commands[command][args[0]] || commands[command].default)(args[0]); + return; } // If no continuation with default turbo command diff --git a/utils/logger.js b/utils/logger.js index aa5fb9b..dc26176 100644 --- a/utils/logger.js +++ b/utils/logger.js @@ -1,5 +1,4 @@ class Logger { - x; static info(message) { console.log(`\x1b[36m✔️ ${message}\x1b[0m`); } @@ -9,7 +8,7 @@ class Logger { } static error(message) { - console.error(`\x1b[31m❌ ${message}\x1b[0m`); + console.error(`\n\x1b[31m❌ ${message}\x1b[0m`); } static warn(message) { From cc0640ded8083cdd507861dbb104ba97242e6e7d Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Sun, 26 Jan 2025 20:07:32 +0530 Subject: [PATCH 6/7] Stop spinner of installWranglerPkg on process exit --- scripts/add-astro.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/add-astro.mjs b/scripts/add-astro.mjs index 397b91a..48ea20f 100644 --- a/scripts/add-astro.mjs +++ b/scripts/add-astro.mjs @@ -193,6 +193,7 @@ const installWranglerPkg = (projectDir, spinner) => { }); addWranglerProcess.on('exit', (code) => { + spinner.stop(); if (code === 0) { Logger.info('Wrangler installed successfully!'); resolve(); From 12290eb342c2cfc33896651cb5ae909705fd567c Mon Sep 17 00:00:00 2001 From: Ayush Makwana Date: Thu, 30 Jan 2025 12:44:23 +0530 Subject: [PATCH 7/7] Improve commands resolution and error handling --- scripts/flarekit.mjs | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/flarekit.mjs b/scripts/flarekit.mjs index 8760da4..8c545a0 100755 --- a/scripts/flarekit.mjs +++ b/scripts/flarekit.mjs @@ -7,24 +7,20 @@ import { addAstroProject } from './add-astro.mjs'; import Logger from '../utils/logger.js'; const rootDir = resolve(dirname(dirname(fileURLToPath(import.meta.url)))); -// Custom commands +// Registered Custom commands const commands = { add: { astro: async () => { try { await addAstroProject(rootDir); } catch (err) { - Logger.error(`Failed to add Astro project`); + Logger.error(`Failed to add Astro project: ${err.message}`); } finally { return; } }, - default: (project) => { - console.log(`Unknown project ${project}...`); - }, - }, - default: (command) => { - console.log(`Unknown command ${command}...`); + // Add new sub commands here + default: (subCommand) => Logger.error(`Unknown subCommand: ${subCommand}`), }, }; @@ -32,16 +28,33 @@ async function main() { // 1. Run setup await init(); - const [, , command = 'default', ...args] = process.argv; + const [, , command, ...args] = process.argv; + + if (!command) { + Logger.error('No command provided.'); + console.log( + `\nUsage:\n- npx flarekit [args]\n- npm run [args]`, + ); + process.exit(1); + } + // Check for custom command if (commands[command]) { - await (commands[command][args[0]] || commands[command].default)(args[0]); - return; + const subCommand = args[0]; + const commandFn = + commands[command][subCommand] || commands[command].default; + + if (typeof commandFn === 'function') { + await commandFn(subCommand); + return; + } else { + Logger.error(`Invalid subcommand: ${subCommand}`); + process.exit(1); + } } - // If no continuation with default turbo command // 2. Build the turbo command + arguments - const turboArgs = [command, args]; // e.g. ['run', 'preview'] or whatever you pass + const turboArgs = [command, ...args]; // e.g. ['run', 'preview'] or whatever you pass console.log(`Executing turbo with args: ${turboArgs.join(' ')}`); // 3. Spawn Turbo