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/UI implementation #52

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@
},
"keywords": [],
"author": "",
"license": "ISC"
"license": "ISC",
"pnpm": {
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
}
}
}
30 changes: 18 additions & 12 deletions packages/cli/command-prompts/getProjectNamePrompt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import inquirer from 'inquirer';
import chalk from 'chalk';
import Enquirer from 'enquirer';
import { CHECK_MARK_COLOR, LEFT_PADDING } from 'stplr-utils';

export const getProjectNamePrompt = async (): Promise<string> =>
(
await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'What is your project named?',
default: 'my-stapled-app',
},
])
).name;
export const getProjectNamePrompt = async (): Promise<string> => {
const enquirer = new Enquirer();
const response = (await enquirer.prompt({
type: 'input',
name: 'name',
message: chalk.whiteBright('What is your project named?'),
initial: 'my-stapled-app',
prefix: LEFT_PADDING,
format(value) {
return `${chalk.hex(CHECK_MARK_COLOR)(value)}`;
},
})) as { name: string };

return response.name;
};
26 changes: 17 additions & 9 deletions packages/cli/command-prompts/overwriteDirectoryPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import inquirer from 'inquirer';
import chalk from 'chalk';
import Enquirer from 'enquirer';
import { CHECK_MARK_COLOR, LEFT_PADDING } from 'stplr-utils';

/**
* Prompts the user to confirm whether they want to overwrite an existing project directory.
Expand All @@ -8,12 +10,18 @@ import inquirer from 'inquirer';
*
**/

export const overwriteDirectoryPrompt = async (projectName: string): Promise<{ overwrite: boolean }> =>
await inquirer.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `The directory "${projectName}" already exists. Do you want to overwrite it?`,
default: false,
export const overwriteDirectoryPrompt = async (projectName: string): Promise<{ overwrite: boolean }> => {
const enquirer = new Enquirer();
const response = (await enquirer.prompt({
type: 'confirm',
name: 'overwrite',
message: chalk.whiteBright(`The directory "${projectName}" already exists. Do you want to overwrite it?`),
initial: false,
prefix: LEFT_PADDING,
format(value) {
return `${chalk.hex(CHECK_MARK_COLOR)(value)}`;
},
]);
})) as { overwrite: boolean };

return response;
};
26 changes: 17 additions & 9 deletions packages/cli/command-prompts/shouldUsePayloadPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import inquirer from 'inquirer';
import chalk from 'chalk';
import Enquirer from 'enquirer';
import { CHECK_MARK_COLOR, LEFT_PADDING } from 'stplr-utils';

/**
* Prompts the user to confirm whether they want to overwrite an existing project directory.
Expand All @@ -8,12 +10,18 @@ import inquirer from 'inquirer';
*
**/

export const shouldUsePayloadPrompt = async (): Promise<{ usePayload: boolean }> =>
await inquirer.prompt([
{
type: 'confirm',
name: 'usePayload',
message: 'Would you like to add Payload to your app?',
default: true,
export const shouldUsePayloadPrompt = async (): Promise<{ usePayload: boolean }> => {
const payloadEnquirer = new Enquirer();
const response = (await payloadEnquirer.prompt({
type: 'confirm',
name: 'usePayload',
message: chalk.whiteBright('Would you like to use Payload?'),
initial: true, // Default value
prefix: LEFT_PADDING, // Removes the default '?' prefix
format(value) {
return `${chalk.hex(CHECK_MARK_COLOR)(value)}`;
},
]);
})) as { usePayload: boolean };

return response;
};
88 changes: 71 additions & 17 deletions packages/cli/command-prompts/unfinishedProjectsChoicePrompt.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import inquirer from 'inquirer';
import Enquirer from 'enquirer';

import chalk from 'chalk';
import { CHECK_MARK_COLOR, LEFT_PADDING, QUESTION_MARK } from 'stplr-utils';
import { ProjectChoice, UnfinishedProject } from '../utils/findUnfinishedProjects';

export type UnfinishedProjectsChoiceAnswers = {
resume: boolean;
unfinishedSelectedProject: string;
};

export type UnfinishedProjectsChoiceResponse = {
resume: boolean;
unfinishedSelectedProject: UnfinishedProject;
};
Expand All @@ -18,21 +26,67 @@ export type UnfinishedProjectsChoiceAnswers = {
export const unfinishedProjectsChoice = async (
unfinishedProjects: UnfinishedProject[],
projectChoices: ProjectChoice[],
): Promise<UnfinishedProjectsChoiceAnswers> =>
await inquirer.prompt([
{
type: 'confirm',
name: 'resume',
message: `We found the following unfinished project(s):\n${unfinishedProjects
.map((p) => `- ${p.projectName}`)
.join('\n')}\nWould you like to resume one of them?`,
default: true,
): Promise<UnfinishedProjectsChoiceResponse> => {
const enquirer = new Enquirer();
const formattedProjectChoices = projectChoices.map((choice) => {
return {
name: choice.name,
value: choice.name,
message: chalk.whiteBright(choice.name),
};
});

// we might need to create custom prompt format like in: https://www.npmjs.com/package/enquirer#-custom-prompts
const shouldResume = (await enquirer.prompt({
type: 'confirm',
name: 'resume',
message: chalk.whiteBright(
`We found the following unfinished project(s):\n${unfinishedProjects
.map((p) => `${LEFT_PADDING} - ${p.projectName}`)
.join('\n')}\n ${LEFT_PADDING}${QUESTION_MARK} Would you like to resume one of them?`,
),
initial: true,
prefix: LEFT_PADDING,
format(value) {
return `${chalk.hex(CHECK_MARK_COLOR)(value)}`;
},
})) as { resume: boolean };

if (!shouldResume.resume) {
return {
resume: false,
unfinishedSelectedProject: unfinishedProjects[0],
};
}

const selectProjectAnswer = (await enquirer.prompt({
type: 'select',
name: 'unfinishedSelectedProject',
message: chalk.whiteBright('Select a project to resume:'),
choices: formattedProjectChoices,
prefix: ' ' + LEFT_PADDING + QUESTION_MARK,
// use it only when we want to resume a project
skip: (state: unknown) => {
if (typeof state === 'object' && state !== null && 'resume' in state) {
console.log('state', state);
return !(state as UnfinishedProjectsChoiceAnswers).resume;
}
console.log('state', state);
return false; // default fallback if not sure
},
{
type: 'list',
name: 'unfinishedSelectedProject',
message: 'Select a project to resume:',
choices: projectChoices,
when: (answers) => answers.resume && unfinishedProjects.length > 1,
format(value) {
return chalk.hex(CHECK_MARK_COLOR)(value);
},
]);
})) as UnfinishedProjectsChoiceAnswers;

const selectedProject = unfinishedProjects.find(
(project) => project.projectName === selectProjectAnswer.unfinishedSelectedProject,
);

const response = {
resume: selectProjectAnswer.resume,
unfinishedSelectedProject: selectedProject,
} as UnfinishedProjectsChoiceResponse;

return response;
};
18 changes: 8 additions & 10 deletions packages/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
import fs from 'fs';
import chalk from 'chalk';
import { Command } from 'commander';
import { logger } from 'stplr-utils';
import { createProject } from 'stplr-core';
import {
checkAuthentication,
checkTools,
displayHeader,
findUnfinishedProjects,
getProjectChoices,
UnfinishedProject,
} from './utils';
import { checkAuthentication, checkTools, findUnfinishedProjects, getProjectChoices, UnfinishedProject } from './utils';
import {
getProjectNamePrompt,
overwriteDirectoryPrompt,
Expand All @@ -33,7 +27,7 @@ program
)
.version('0.1.0')
.hook('preAction', () => {
displayHeader();
logger.displayHeader();
})
.option('-n, --name <name>', 'Set the name of the project')
.option(
Expand All @@ -53,6 +47,7 @@ const createAction = async (options: Flags) => {

const unfinishedProjects: UnfinishedProject[] = findUnfinishedProjects(currentDir);

logger.withLabel('dir', 'Your project name and location');
// If no project name is provided, and there are unfinished projects, we prompt the user to resume one of them
if (!options.name && unfinishedProjects.length > 0) {
const projectChoices = getProjectChoices(unfinishedProjects);
Expand Down Expand Up @@ -104,15 +99,18 @@ const createAction = async (options: Flags) => {

// Clear the directory if overwrite is confirmed
fs.rmSync(projectDir, { recursive: true, force: true });
console.log(chalk.yellow(`The directory "${projectName}" has been cleared.`));
logger.log(chalk.yellow(`The directory "${projectName}" has been cleared.`));
}

// Skip Payload if specified by the flag
logger.withLabel('cms', 'CMS setup');
const payloadAnswer = options.skipPayload ? { usePayload: false } : await shouldUsePayloadPrompt();

const finalOptions = { name: projectName, shouldDeploy, ...payloadAnswer };
if (shouldDeploy) {
logger.withLabel('auth', 'Authentication status');
await checkAuthentication();
logger.withLabel('stapler', 'Tooling status');
await checkTools();
}

Expand Down
5 changes: 3 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
"dependencies": {
"chalk": "^5.3.0",
"commander": "^12.1.0",
"enquirer": "^2.4.1",
"gradient-string": "^3.0.0",
"inquirer": "^10.2.2"
},
"devDependencies": {
"stplr-core": "workspace:*",
"stplr-utils": "workspace:*",
"@types/inquirer": "^9.0.7",
"@types/node": "^22.5.4",
"stplr-core": "workspace:*",
"stplr-utils": "workspace:*",
"tsup": "^8.2.4",
"typescript": "^5.6.2"
}
Expand Down
14 changes: 14 additions & 0 deletions packages/core/installMachine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
updateVercelProjectSettings,
} from './installSteps/vercel';
import { shouldDeploy } from './installSteps/shouldDeploy';
import { logger } from 'stplr-utils';

const isStepCompleted = (stepName: keyof StepsCompleted) => {
return ({ context }: { context: InstallMachineContext; event: AnyEventObject }) => {
Expand Down Expand Up @@ -349,6 +350,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
createTurboActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('turborepo', 'Creating Turbo project');
await createTurbo(input.stateData.options.name);
process.chdir(input.projectDir);
input.stateData.stepsCompleted.createTurbo = true;
Expand All @@ -362,6 +364,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
modifyGitignoreActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('git', 'Modifying .gitignore');
await modifyGitignore('.initializeRcFile');
input.stateData.stepsCompleted.modifyGitignore = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -374,6 +377,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
installTailwindActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('tailwind', 'Installing Tailwind CSS');
const currentDir = process.cwd();
await installTailwind(currentDir);
input.stateData.stepsCompleted.installTailwind = true;
Expand All @@ -399,6 +403,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
installSupabaseActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('supabase', 'Installing Supabase');
const currentDir = process.cwd();
await installSupabase(currentDir);
input.stateData.stepsCompleted.installSupabase = true;
Expand All @@ -412,6 +417,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
installPayloadActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('payload', 'Preparing Payload');
await preparePayload();
input.stateData.stepsCompleted.installPayload = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -424,6 +430,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
createDocFilesActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('stapler', 'Creating documentation files');
await createDocFiles();
input.stateData.stepsCompleted.createDocFiles = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -436,6 +443,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
prettifyCodeActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('prettier', 'Prettifying code');
await prettify();
input.stateData.stepsCompleted.prettifyCode = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -459,6 +467,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
initializeRepositoryActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('github', 'Initializing GitHub repository');
await initializeRepository({
projectName: input.stateData.options.name,
stateData: input.stateData,
Expand Down Expand Up @@ -486,6 +495,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
createSupabaseProjectActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('supabase', 'Creating Supabase project');
await createSupabaseProject(input.stateData.githubCandidateName);
input.stateData.stepsCompleted.createSupabaseProject = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -498,6 +508,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
chooseVercelTeamActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('vercel', 'Managing your Vercel');
await chooseVercelTeam();
input.stateData.stepsCompleted.chooseVercelTeam = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand Down Expand Up @@ -534,6 +545,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
connectSupabaseProjectActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('supabase', 'Connecting Supabase project');
const currentDir = process.cwd();
await connectSupabaseProject(input.stateData.githubCandidateName, currentDir);
input.stateData.stepsCompleted.connectSupabaseProject = true;
Expand All @@ -547,6 +559,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
deployVercelProjectActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('vercel', 'Deploying Vercel project');
await deployVercelProject(input.stateData);
input.stateData.stepsCompleted.deployVercelProject = true;
saveStateToRcFile(input.stateData, input.projectDir);
Expand All @@ -559,6 +572,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
prepareDrinkActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
logger.withLabel('stapler', 'Preparing your drink');
const {
projectName,
prettyDeploymentUrl,
Expand Down
Loading