Skip to content

Commit

Permalink
Refactor recipes + add nextjs recipe (#815)
Browse files Browse the repository at this point in the history
* Refactor recipes

* Add nextjs recipe

* Fix package project name

* Add basic example auth usage

* Fix lint

* Add tailwind, srcDir, and js options for nextjs

* Fix build
  • Loading branch information
jaclarke authored Jan 3, 2024
1 parent a9f782a commit 5eabf23
Show file tree
Hide file tree
Showing 86 changed files with 4,215 additions and 192 deletions.
66 changes: 11 additions & 55 deletions packages/create/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,22 @@
#!/usr/bin/env node

import * as p from "@clack/prompts";
import process from "node:process";
import path from "node:path";
import debug from "debug";

import * as utils from "./utils.js";
import { recipes, type Framework } from "./recipes/index.js";

const logger = debug("@edgedb/create:main");
import { baseRecipe, recipes as _recipes } from "./recipes/index.js";

async function main() {
const packageManager = utils.getPackageManager();
logger({ packageManager });
const baseOptions = await baseRecipe.getOptions();
const recipeOptions: any[] = [];

const setup = await p.group(
{
projectName: () =>
p.text({
message: "What is the name of your project or application?",
}),
framework: () =>
p.select<{ value: Framework; label: string }[], Framework>({
message: "What web framework should be used?",
options: [
{ value: "next", label: "Next.js" },
{ value: "remix", label: "Remix" },
{ value: "express", label: "Express" },
{ value: "node-http", label: "Node HTTP Server" },
{ value: "none", label: "None" },
],
}),
useEdgeDBAuth: () =>
p.confirm({
message: "Use the EdgeDB Auth extension?",
initialValue: true,
}),
shouldGitInit: () =>
p.confirm({
message: "Initialize a git repository and stage changes?",
initialValue: true,
}),
shouldInstall: () =>
p.confirm({
message: `Install dependencies with ${packageManager}?`,
initialValue: true,
}),
},
{
onCancel: () => {
process.exit(1);
},
}
const recipes = _recipes.filter(
(recipe) => !(recipe.skip?.(baseOptions) ?? false)
);

logger(setup);

for (const recipe of recipes) {
await recipe({
...setup,
projectDir: path.resolve(process.cwd(), setup.projectName),
});
recipeOptions.push(await recipe.getOptions?.(baseOptions));
}

await baseRecipe.apply(baseOptions);
for (let i = 0; i < recipes.length; i++) {
await recipes[i].apply(baseOptions, recipeOptions[i]);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "replace-me",
"version": "0.1.0",
"type": "module",
"private": true,
"dependencies": {
"edgedb": "1.x"
Expand Down
106 changes: 106 additions & 0 deletions packages/create/src/recipes/_base/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import process from "node:process";
import fs from "node:fs/promises";
import path from "node:path";

import debug from "debug";
import * as p from "@clack/prompts";

import { updatePackage } from "write-package";

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

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

const recipe: BaseRecipe = {
async getOptions() {
const packageManager = utils.getPackageManager();
logger({ packageManager });

const opts = await p.group(
{
projectName: () =>
p.text({
message: "What is the name of your project or application?",
}),
framework: () =>
p.select<{ value: Framework; label: string }[], Framework>({
message: "What web framework should be used?",
options: [
{ value: "next", label: "Next.js" },
{ value: "remix", label: "Remix" },
{ value: "express", label: "Express" },
{ value: "node-http", label: "Node HTTP Server" },
{ value: "none", label: "None" },
],
}),
useEdgeDBAuth: () =>
p.confirm({
message: "Use the EdgeDB Auth extension?",
initialValue: true,
}),
},
{
onCancel: () => {
process.exit(1);
},
}
);

return {
...opts,
packageManager,
projectDir: path.resolve(process.cwd(), opts.projectName),
};
},
async apply({ projectDir, projectName }: BaseOptions) {
logger("Running base recipe");
try {
const projectDirStat = await fs.stat(projectDir);
logger({ projectDirStat });

if (!projectDirStat.isDirectory()) {
throw new Error(
`Target project directory ${projectDir} is not a directory`
);
}
const files = await fs.readdir(projectDir);
if (files.length > 0) {
throw new Error(`Target project directory ${projectDir} is not empty`);
}
} catch (err) {
if (
typeof err === "object" &&
err !== null &&
"code" in err &&
err.code === "ENOENT"
) {
await fs.mkdir(projectDir, { recursive: true });
logger(`Created project directory: ${projectDir}`);
} else {
throw err;
}
}

const dirname = path.dirname(new URL(import.meta.url).pathname);

logger("Copying files");
await fs.copyFile(
path.resolve(dirname, "./_eslint.config.js"),
path.resolve(projectDir, "eslint.config.js")
);
await fs.copyFile(
path.resolve(dirname, "./_package.json"),
path.resolve(projectDir, "package.json")
);
await fs.copyFile(
path.resolve(dirname, "./_tsconfig.json"),
path.resolve(projectDir, "tsconfig.json")
);

logger("Writing package.json");
await updatePackage(projectDir, { name: projectName });
},
};

export default recipe;
50 changes: 50 additions & 0 deletions packages/create/src/recipes/_edgedb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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";

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 8 in packages/create/src/recipes/_edgedb/index.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

All imports in the declaration are only used as types. Use `import type`

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

const recipe: Recipe = {
async apply({ projectDir, useEdgeDBAuth }: BaseOptions) {
logger("Running edgedb recipe");

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 });

throw err;
}
spinner.stop(`EdgeDB v${serverVersion} project initialized`);

if (useEdgeDBAuth) {
logger("Adding auth extension to project");

spinner.start("Enabling auth extension in EdgeDB schema");
const filePath = path.resolve(projectDir, "./dbschema/default.esdl");
const data = await fs.readFile(filePath, "utf8");
await fs.writeFile(filePath, `using extension auth;\n\n${data}`);
spinner.stop("Auth extension enabled in EdgeDB schema");
}
},
};

export default recipe;
34 changes: 34 additions & 0 deletions packages/create/src/recipes/_install/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import debug from "debug";
import * as p from "@clack/prompts";

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

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 5 in packages/create/src/recipes/_install/index.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

All imports in the declaration are only used as types. Use `import type`

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

interface InstallOptions {
shouldGitInit: boolean;
shouldInstall: boolean;
}

const recipe: Recipe<InstallOptions> = {
getOptions({ packageManager }: BaseOptions) {
return p.group({
shouldGitInit: () =>
p.confirm({
message: "Initialize a git repository and stage changes?",
initialValue: true,
}),
shouldInstall: () =>
p.confirm({
message: `Install dependencies with ${packageManager}?`,
initialValue: true,
}),
});
},
async apply(baseOptions: BaseOptions, recipeOptions: InstallOptions) {
logger("Running install recipe");
},
};

export default recipe;
63 changes: 0 additions & 63 deletions packages/create/src/recipes/base/index.ts

This file was deleted.

49 changes: 0 additions & 49 deletions packages/create/src/recipes/edgedb/index.ts

This file was deleted.

Loading

0 comments on commit 5eabf23

Please sign in to comment.