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

feat(inquirer): update codebase from inquirer to @inquirer/prompts #69

Merged
merged 2 commits into from
Nov 4, 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
1,442 changes: 526 additions & 916 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@
"provenance": true
},
"devDependencies": {
"@types/dedent": "^0.7.0",
"@types/inquirer": "9.0.1",
"@types/node": "18.7.1",
"@types/node-fetch": "2.6.2",
"@types/node": "^22.8.6",
"@types/tar-fs": "2.0.1",
"@typescript-eslint/eslint-plugin": "5.36.2",
"@typescript-eslint/parser": "5.36.2",
Expand All @@ -59,15 +56,12 @@
"vitest": "0.23.1"
},
"dependencies": {
"@inquirer/prompts": "^7.0.1",
"@molt/command": "^0.7.0",
"chalk": "^5.0.1",
"dedent": "^0.7.0",
"gunzip-maybe": "^1.4.2",
"inquirer": "^9.1.0",
"inquirer-search-list": "^1.2.6",
"node-fetch": "^3.2.10",
"ora": "^6.1.2",
"tar-fs": "^2.1.1",
"tar-fs": "^3.0.6",
"valid-filename": "^4.0.0",
"zod": "^3.21.2"
},
Expand All @@ -76,4 +70,4 @@
"path": "./node_modules/cz-conventional-changelog"
}
}
}
}
4 changes: 2 additions & 2 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default class Cli {

// select template from list of projects
if (!this.args.template.length) {
this.args.template = await prompts.getTemplate(this.projects);
this.args.template = (await prompts.getTemplate(this.projects)) as string;
}

if (!this.args.install) {
Expand All @@ -126,7 +126,7 @@ export default class Cli {
}

if (!this.args.path.length) {
this.args.path = await prompts.getProjectDirectory();
this.args.path = ".";
}

return this.args;
Expand Down
91 changes: 28 additions & 63 deletions src/cli/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,124 +1,95 @@
import logger from "../helpers/logger";
import validate from "./validation";
import { confirm, input, select } from "@inquirer/prompts";
import chalk from "chalk";
import inquirer from "inquirer";
import SearchList from "inquirer-search-list";

inquirer.registerPrompt("search-list", SearchList);
import logger from "../helpers/logger";
import validate from "./validation";

const selectStarterOrExample = async (): Promise<string> => {
const { starter } = await inquirer.prompt({
type: "list",
return await select({
message: `How would you like to start your new project?`,
name: "starter",
choices: [
{
name: `Prisma Starter (Recommended)\n\xa0\xa0- ${chalk.gray(
"The Prisma Starter is pre-configured with Prisma ORM, Accelerate, Pulse, and a Prisma Postgres database.",
)}`,
name: `Prisma Starter (Recommended)`,
value: "starter",
description:
"The Prisma Starter is pre-configured with Prisma ORM, Accelerate, Pulse, and a Prisma Postgres database.",
},
{
name: "Explore other examples",
value: "example",
description:
"Our curated list of examples from the prisma/prisma-examples GitHub repository, showing how to use Prisma ORM with various other tools and frameworks.",
},
],
});
return starter;
};

const getTemplate = async (projects: string[]): Promise<string> => {
const getTemplate = async (projects: string[]) => {
logger.success(
`\nDon't see what you're looking for? Request a new template here:\n\xa0\xa0➡ ${chalk.underline.gray(
"https://pris.ly/prisma-examples-suggestion",
)}\n`,
);
const { template } = await inquirer.prompt({
// @ts-expect-error Inquirer doesn't register the type.
type: "search-list",
const result = await select({
message: `Which template would you like to use?`,
name: "template",
choices: projects,
validate: (answer) => {
try {
validate.project(projects, answer);
} catch (e) {
return false;
}
return true;
},
});

return template;
return result;
};

const selectORMorPDP = async (): Promise<string> => {
const selectORMorPDP = async () => {
logger.success(
`\nThese options correspond to the root directories in the prisma-examples repository:\n`,
);
const { start } = await inquirer.prompt({
type: "list",
return await select({
message: `Which Prisma examples would you like to explore?`,
name: "start",
choices: [
{
name: `Prisma ORM\n\xa0\xa0- ${chalk.gray(
"Define Prisma schema and run queries",
)}`,
name: `Prisma ORM`,
value: "orm",
description: "Define Prisma schema and run queries",
},
{
name: `Prisma Accelerate\n\xa0\xa0- ${chalk.gray(
"Perform caching and connection pooling",
)}`,
name: `Prisma Accelerate`,
value: "accelerate",
description: "Perform caching and connection pooling",
},
{
name: `Prisma Pulse\n\xa0\xa0- ${chalk.gray(
"Monitor and react to real-time database changes",
)}`,
name: `Prisma Pulse`,
value: "pulse",
description: "Monitor and react to real-time database changes",
},
{
name: `Prisma Optimize\n\xa0\xa0- ${chalk.gray(
"Analyze and improve query performance",
)}`,
name: `Prisma Optimize`,
value: "optimize",
description: "Analyze and improve query performance",
},
],
});

return start;
};

const getInstallSelection = async (): Promise<boolean> => {
const { packages } = await inquirer.prompt({
type: "confirm",
const getInstallSelection = async () => {
return confirm({
message: `Should we automatically install packages for you?`,
name: "packages",
default: true,
});
return Boolean(packages);
};

const selectManager = async (): Promise<"npm" | "yarn" | "pnpm"> => {
const { manager } = await inquirer.prompt({
type: "list",
return await select({
message: "Which package manager do you prefer?",
name: "manager",
default: process.env.npm_config_user_agent,
choices: ["npm", "yarn", "pnpm"],
});
return manager;
};

const getProjectName = async (defaultValue = ""): Promise<string> => {
const { dirname } = await inquirer.prompt({
type: "input",
return await input({
message: "What should the project folder be named?",
name: "dirname",
default: defaultValue,
filter: (input) => input.replace("/", "_").trim(),
transformer: (input) => input.replace("/", "_").trim(),
// filter: (input) => input.replace("/", "_").trim(),
validate(answer) {
try {
validate.directoryName(answer);
Expand All @@ -129,18 +100,12 @@ const getProjectName = async (defaultValue = ""): Promise<string> => {
return true;
},
});
return dirname;
};

const getProjectDirectory = async (): Promise<string> => {
return ".";
};

export default {
selectStarterOrExample,
selectORMorPDP,
getInstallSelection,
getProjectDirectory,
getProjectName,
selectManager,
getTemplate,
Expand Down
3 changes: 1 addition & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ export const EXAMPLES_REPO_URL =
export const EXAMPLES_REPO_TAR =
"https://codeload.github.com/prisma/prisma-examples/tar.gz/latest";

export const EXAMPLES_REPO_INTERCEPTOR = // "http://localhost:3000/api/interceptor";
export const EXAMPLES_REPO_INTERCEPTOR =
"https://www.try-prisma-analytics.com/api/interceptor";
// "https://www.try-prisma-analytics.vercel.app/api/interceptor";

export const EXAMPLES_DIR_ACCEPT = [
"typescript",
Expand Down
79 changes: 38 additions & 41 deletions src/helpers/download.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { EXAMPLES_REPO_TAR, EXAMPLES_REPO_INTERCEPTOR } from "../constants";
import { CliInput } from "../types";
import gunzip from "gunzip-maybe";
import fetch from "node-fetch";
import ora from "ora";
import path from "path";
import stream from "stream";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
import tar from "tar-fs";
import { promisify } from "util";

const pipeline = promisify(stream.pipeline);

export default async function download(options: CliInput): Promise<void> {
if (!options.template.length) {
Expand All @@ -17,69 +14,69 @@ export default async function download(options: CliInput): Promise<void> {
);
}

const spinner = ora();
spinner.start(`Downloading and extracting the ${options.template} project`);
const templateName =
options.template === "databases/prisma-postgres"
? "Prisma Starter"
: options.template;

// Download the repo via the interceptor
let response = await fetch(EXAMPLES_REPO_INTERCEPTOR, {
method: "POST",
body: JSON.stringify(options),
});
const spinner = ora();
spinner.start(`Downloading and extracting the ${templateName} project`);

if (response.status !== 200) {
// Something went wrong with the interceptor,
// try fetching from GitHub directly ...
response = await fetch(EXAMPLES_REPO_TAR, {
method: "GET",
try {
// Download the repo via the interceptor
let response = await fetch(EXAMPLES_REPO_INTERCEPTOR, {
method: "POST",
body: JSON.stringify(options),
});

if (response.status !== 200) {
spinner.stopAndPersist();
if (!response.ok) {
// fallback to GitHub direct download
response = await fetch(EXAMPLES_REPO_TAR, {
method: "GET",
});
}

if (!response.ok || !response.body) {
throw new Error(
`Something went wrong when fetching prisma/prisma-examples. Received a status code ${response.status}.`,
`Failed to fetch prisma/prisma-examples. Status: ${response.status}`,
);
}
}

try {
await pipeline(
// Unzip it
response.body?.pipe(gunzip()),
// Extract the stuff into this directory
Readable.from(response.body),
gunzip(),
tar.extract(`${options.path}/${options.name}`, {
map(header) {
const originalDirName = header.name.split("/")[0];
header.name = header.name.replace(`${originalDirName}/`, "");
options.template = options.template

const normalizedTemplate = options.template
.split(path.sep)
.join(path.posix.sep);
if (options.template) {
if (header.name.startsWith(`${options.template}/`)) {
header.name = header.name.replace(options.template, "");
} else {
header.name = "[[ignore-me]]";
}

if (header.name.startsWith(`${normalizedTemplate}/`)) {
header.name = header.name.replace(normalizedTemplate, "");
} else {
header.name = "[[ignore-me]]";
}

return header;
},
ignore(_filename, header) {
if (!header) {
throw new Error(`Header is undefined`);
}

return header.name === "[[ignore-me]]";
},
readable: true,
writable: true,
}),
);
spinner.succeed(
`Downloaded and extracted the ${options.template} project in ${options.path}/${options.name}.\n`,
);
} catch (e) {
spinner.stopAndPersist();
} catch (error) {
spinner.fail();
throw new Error(
`Something went wrong when extracting the files from the repository tar file.`,
`Failed to download or extract files: ${(error as Error).message}`,
);
}
spinner.succeed(
`Downloaded and extracted the ${options.template} project in ${options.path}/${options.name}.\n`,
);
}
1 change: 0 additions & 1 deletion src/helpers/getProjects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { EXAMPLES_DIR_ACCEPT, EXAMPLES_REPO_URL } from "../constants";
import fetch from "node-fetch";

export default async function getProjects() {
const result = await fetch(EXAMPLES_REPO_URL);
Expand Down
5 changes: 2 additions & 3 deletions src/helpers/logger.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import chalk from "chalk";
import dedent from "dedent";

export default {
error: (message: string) => {
console.log(chalk.redBright(dedent(message)));
console.log(chalk.redBright(message));
},
success: (message: string) => {
console.log(dedent(message));
console.log(message);
},
};
Loading
Loading