From b76c9873aa6b4963f5e27b4a3ac8909f543b76d4 Mon Sep 17 00:00:00 2001 From: Spenser Hale Date: Sun, 14 Apr 2024 18:13:24 -0700 Subject: [PATCH 1/3] Updating node version check CLI Updating README to have version check command print both node and npm versions in one go. Currently when you copy command and paste into terminal only prints one. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 79302c50..75bce944 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,7 @@ In just a few short steps we will set up a project containing a Hatchify fronten 1. Ensure you’re using [node 18 and npm 9 or above](https://nodejs.org/en/download) ```bash - node -v - npm -v + echo Node: $(node -v) && echo npm: $(npm -v) ``` 2. Create a new project: From aa4d55f5b804da8dd7e2d160a77d1b4a6dcc1706 Mon Sep 17 00:00:00 2001 From: Spenser Hale Date: Sun, 14 Apr 2024 18:14:15 -0700 Subject: [PATCH 2/3] Updating CLI options order Updating Constants for Backends and Databases to provide the preferred option as the default --- packages/create/src/constants.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/create/src/constants.ts b/packages/create/src/constants.ts index 9f030758..f476e8fe 100644 --- a/packages/create/src/constants.ts +++ b/packages/create/src/constants.ts @@ -2,13 +2,6 @@ import { blue, green, yellow } from "kolorist" import type { Database, Backend, Frontend } from "./types" export const BACKENDS: Record = { - EXPRESS: { - name: "express", - display: "Express", - color: yellow, - dependencies: ["express", "@hatchifyjs/express"], - devDependencies: [], - }, KOA: { name: "koa", display: "Koa", @@ -16,16 +9,16 @@ export const BACKENDS: Record = { dependencies: ["koa", "@hatchifyjs/koa"], devDependencies: ["@types/koa", "koa-connect"], }, + EXPRESS: { + name: "express", + display: "Express", + color: yellow, + dependencies: ["express", "@hatchifyjs/express"], + devDependencies: [], + }, } export const DATABASES: Record = { - POSTGRES: { - name: "postgres", - display: "Postgres", - color: yellow, - dependencies: ["pg", "dotenv"], - devDependencies: ["@types/pg"], - }, SQLITE: { name: "sqlite", display: "SQLite", @@ -33,6 +26,13 @@ export const DATABASES: Record = { dependencies: ["sqlite3"], devDependencies: [], }, + POSTGRES: { + name: "postgres", + display: "Postgres", + color: yellow, + dependencies: ["pg", "dotenv"], + devDependencies: ["@types/pg"], + }, } export const FRONTENDS: Record = { From 6e515d1266b1349224b3ff58881e7d9bd6e05033 Mon Sep 17 00:00:00 2001 From: Spenser Hale Date: Sun, 14 Apr 2024 18:18:37 -0700 Subject: [PATCH 3/3] Fixing create command compatibility Fixing three bugs: 1) If your project name has spaces, the command will fail. Now uses packageName as folder name. 2) npm create vite@latest never runs on node version 20+. Now uses exec command instead of spawn.sync. 3) Reading the template package.json asks for file before the file is fully written to OS. Now has new function readFileWithRetries to wait for file is ready. --- packages/create/src/index.ts | 25 ++++++++++++++----------- packages/create/src/util.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/create/src/index.ts b/packages/create/src/index.ts index 81eeb27b..b4c8b17e 100755 --- a/packages/create/src/index.ts +++ b/packages/create/src/index.ts @@ -5,18 +5,19 @@ import { fileURLToPath } from "node:url" import minimist from "minimist" import prompts from "prompts" import { red, reset } from "kolorist" +import { exec } from "child_process" import { copyDir, - emptyDir, + emptyDir, execCommandExitOnError, formatTargetDir, isEmpty, isValidPackageName, - pkgFromUserAgent, + pkgFromUserAgent, readFileWithRetries, replaceStringInFile, runCommand, toValidPackageName, } from "./util" -import { DATABASES, BACKENDS, FRONTENDS } from "./constants" +import { BACKENDS, DATABASES, FRONTENDS } from "./constants" import type { Database } from "./types" // Avoids autoconversion to number of the project name by defining that the args @@ -202,6 +203,8 @@ async function init() { databaseHost, )}:${databasePort}/${encodeURIComponent(databaseName)}`) + targetDir = packageName || targetDir; + const root = path.join(cwd, targetDir) if (overwrite) { @@ -215,15 +218,15 @@ async function init() { console.log(`\nScaffolding project in ${root}...`) - runCommand( - `npm create vite@latest ${targetDir} -- --template react-ts`, - cwd, - true, - ) + execCommandExitOnError(`npm create vite@latest "${targetDir}" -- --template react-ts`) - const templatePackage = JSON.parse( - await fs.promises.readFile(path.join(root, "package.json"), "utf-8"), - ) + let templatePackage; + try { + templatePackage = JSON.parse(await readFileWithRetries(path.join(root, "package.json"))); + } catch (e) { + console.error(e) + process.exit(1) + } const backendTemplateDir = path.resolve( fileURLToPath(import.meta.url), diff --git a/packages/create/src/util.ts b/packages/create/src/util.ts index 35be1f35..ee1ad262 100644 --- a/packages/create/src/util.ts +++ b/packages/create/src/util.ts @@ -1,6 +1,7 @@ import fs from "node:fs" import path from "node:path" import spawn from "cross-spawn" +import { exec } from "child_process" const renameFiles: Record = { _gitignore: ".gitignore", @@ -20,6 +21,34 @@ export async function replaceStringInFile( ) } +export async function readFileWithRetries(filePath: string, retries: number = 5, delay: number = 1000): Promise { + for (let attempt = 1; attempt <= retries; attempt++) { + if (fs.existsSync(filePath)) { + return await fs.promises.readFile(filePath, "utf-8"); + } + await new Promise(resolve => setTimeout(resolve, attempt * delay)); + } + throw new Error(`${filePath} could not be found after ${retries} attempts`); +} + +export function execCommandExitOnError( + command: string, + args: string[] = [], +): void { + exec(`${command} ${args.join(" ")}`, + (error, stdout, stderr) => { + if (error) { + console.error(`${command} error: ${error.message}`) + process.exit(1) + } + if (stderr) { + console.error(`${command} stderr: ${stderr}`) + process.exit(1) + } + }, + ) +} + export function runCommand( fullCommand: string, cwd: string,