Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EdgeDB create-app recipe #818

Merged
merged 7 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 100 additions & 22 deletions packages/create/src/recipes/_edgedb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,116 @@ import * as p from "@clack/prompts";
import fs from "node:fs/promises";
import path from "node:path";
import debug from "debug";
import util from "node:util";
import childProcess from "node:child_process";

import { BaseOptions, Recipe } from "../types.js";
import type { BaseOptions, Recipe } from "../types.js";
import { copyTemplateFiles, execInLoginShell } from "../../utils.js";

const logger = debug("@edgedb/create:recipe:edgedb");
const exec = util.promisify(childProcess.exec);

const recipe: Recipe = {
async apply({ projectDir, useEdgeDBAuth }: BaseOptions) {
interface EdgeDBOptions {
initializeProject: boolean;
}

const recipe: Recipe<EdgeDBOptions> = {
getOptions() {
return p.group({
initializeProject: () =>
p.confirm({
message: "Initialize a new EdgeDB project with edgedb project init?",
initialValue: true,
}),
});
},

async apply(
{ projectDir, useEdgeDBAuth }: BaseOptions,
{ initializeProject }: EdgeDBOptions
) {
logger("Running edgedb recipe");
logger("Checking for existing EdgeDB CLI");

const spinner = p.spinner();

spinner.start("Initializing EdgeDB project");
await exec("edgedb project init --non-interactive", { cwd: projectDir });
const { stdout, stderr } = await exec(
"edgedb query 'select sys::get_version_as_str()'",
{ cwd: projectDir }
);
const serverVersion = JSON.parse(stdout.trim());
logger(`EdgeDB version: ${serverVersion}`);
if (serverVersion === "") {
const err = new Error(
"There was a problem initializing the EdgeDB project"
);
spinner.stop(err.message);
logger({ stdout, stderr });
if (initializeProject) {
let edgedbCliVersion: string | null = null;
let shouldInstallCli: boolean | symbol = true;
try {
const { stdout } = await execInLoginShell("edgedb --version");
edgedbCliVersion = stdout.trim();
logger(edgedbCliVersion);
} catch (error) {
logger("No EdgeDB CLI detected");
shouldInstallCli = await p.confirm({
message:
"The EdgeDB CLI is required to initialize a project. Install now?",
initialValue: true,
});
}

if (edgedbCliVersion === null) {
if (shouldInstallCli === false) {
logger("User declined to install EdgeDB CLI");
throw new Error("EdgeDB CLI is required");
}

logger("Installing EdgeDB CLI");

throw err;
spinner.start("Installing EdgeDB CLI");
const { stdout, stderr } = await execInLoginShell(
"curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh -s -- -y"
);
logger({ stdout, stderr });
spinner.stop("EdgeDB CLI installed");
}

try {
const { stdout } = await execInLoginShell("edgedb --version");
edgedbCliVersion = stdout.trim();
logger(edgedbCliVersion);
} catch (error) {
logger("EdgeDB CLI could not be installed.");
logger(error);
throw new Error("EdgeDB CLI could not be installed.");
}

spinner.start("Initializing EdgeDB project");
try {
await execInLoginShell("edgedb project init --non-interactive", {
cwd: projectDir,
});
const { stdout, stderr } = await execInLoginShell(
"edgedb query 'select sys::get_version_as_str()'",
{ cwd: projectDir }
);
const serverVersion = JSON.parse(stdout.trim());
logger(`EdgeDB server version: ${serverVersion}`);
if (serverVersion === "") {
const err = new Error(
"There was a problem initializing the EdgeDB project"
);
spinner.stop(err.message);
logger({ stdout, stderr });

throw err;
}
spinner.stop(`EdgeDB v${serverVersion} project initialized`);
} catch (error) {
logger(error);
throw error;
} finally {
spinner.stop();
}
} else {
logger("Skipping edgedb project init");
logger("Copying basic EdgeDB project files");

const dirname = path.dirname(new URL(import.meta.url).pathname);
await copyTemplateFiles(
path.resolve(dirname, "./template"),
projectDir,
new Set()
);
}
spinner.stop(`EdgeDB v${serverVersion} project initialized`);

if (useEdgeDBAuth) {
logger("Adding auth extension to project");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module default {

}
3 changes: 1 addition & 2 deletions packages/create/src/recipes/_install/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import debug from "debug";
import * as p from "@clack/prompts";

import * as utils from "../../utils.js";
import { BaseOptions, Recipe } from "../types.js";
import type { BaseOptions, Recipe } from "../types.js";

const logger = debug("@edgedb/create:recipe:install");

Expand Down
2 changes: 1 addition & 1 deletion packages/create/src/recipes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import _install from "./_install/index.js";
import express from "./express/index.js";
import nextjs from "./nextjs/index.js";

import { Recipe } from "./types.js";
import { type Recipe } from "./types.js";

export { baseRecipe };

Expand Down
32 changes: 32 additions & 0 deletions packages/create/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import process from "node:process";
import fs from "node:fs/promises";
import { type Dirent } from "node:fs";
import path from "node:path";
import { spawn, type SpawnOptionsWithoutStdio } from "node:child_process";

export type PackageManager = "npm" | "yarn" | "pnpm" | "bun";

Expand Down Expand Up @@ -117,3 +118,34 @@ async function _walkDir(
}
}
}

export async function execInLoginShell(
command: string,
options?: SpawnOptionsWithoutStdio
): Promise<{ stdout: string; stderr: string }> {
return new Promise((resolve, reject) => {
let stdout = "";
let stderr = "";
const child = spawn("/bin/bash", ["-l", "-c", command], options);
child.stdout.on("data", (data) => {
stdout += data;
});
child.stderr.on("data", (data) => {
stderr += data;
});
child.on("close", (code) => {
if (code !== 0) {
reject(
new Error(
`\
Command "${command}" exited with code ${code}
stderr: ${stderr}
stdout: ${stdout}`
)
);
} else {
resolve({ stdout, stderr });
}
});
});
}
Loading