From dc2c32e19a8f926d35245429bb909460946154b5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Mejna Date: Wed, 11 Dec 2024 15:34:32 +0100 Subject: [PATCH] feat(setup): allow for local setup only without connecting to the tools (#51) --- .changeset/pink-steaks-push.md | 6 + .changeset/pre.json | 1 + packages/cli/CHANGELOG.md | 6 + packages/cli/README.md | 47 +++++ .../command-prompts/getProjectNamePrompt.ts | 13 ++ packages/cli/command-prompts/index.ts | 4 + .../overwriteDirectoryPrompt.ts | 19 ++ .../command-prompts/shouldUsePayloadPrompt.ts | 19 ++ .../unfinishedProjectsChoicePrompt.ts | 38 ++++ packages/cli/index.ts | 171 +++++++----------- packages/cli/package.json | 2 +- packages/cli/utils/displayHeader.ts | 24 +++ packages/cli/utils/findUnfinishedProjects.ts | 11 ++ packages/cli/utils/index.ts | 4 + packages/core/CHANGELOG.md | 6 + packages/core/index.ts | 4 +- packages/core/installMachine/index.ts | 79 +++++--- .../installSteps/github/index.ts | 6 + .../installSteps/homepage/index.ts | 1 + .../installSteps/payload/index.ts | 7 + .../installSteps/prettier/index.ts | 1 + .../installSteps/shouldDeploy/index.ts | 1 + .../installSteps/shouldDeploy/shouldDeploy.ts | 33 ++++ .../installSteps/stapler/index.ts | 4 + .../installSteps/stapler/prepareDrink.ts | 17 +- .../installSteps/supabase/index.ts | 9 + .../installSteps/tailwind/index.ts | 1 + .../installSteps/turbo/index.ts | 1 + .../installSteps/vercel/index.ts | 6 + packages/core/package.json | 2 +- packages/core/types.ts | 2 + packages/core/utils/rcFileManager/index.ts | 9 +- packages/utils/logger.ts | 6 + 33 files changed, 424 insertions(+), 136 deletions(-) create mode 100644 .changeset/pink-steaks-push.md create mode 100644 packages/cli/command-prompts/getProjectNamePrompt.ts create mode 100644 packages/cli/command-prompts/index.ts create mode 100644 packages/cli/command-prompts/overwriteDirectoryPrompt.ts create mode 100644 packages/cli/command-prompts/shouldUsePayloadPrompt.ts create mode 100644 packages/cli/command-prompts/unfinishedProjectsChoicePrompt.ts create mode 100644 packages/cli/utils/displayHeader.ts create mode 100644 packages/cli/utils/index.ts create mode 100644 packages/core/installMachine/installSteps/github/index.ts create mode 100644 packages/core/installMachine/installSteps/homepage/index.ts create mode 100644 packages/core/installMachine/installSteps/payload/index.ts create mode 100644 packages/core/installMachine/installSteps/prettier/index.ts create mode 100644 packages/core/installMachine/installSteps/shouldDeploy/index.ts create mode 100644 packages/core/installMachine/installSteps/shouldDeploy/shouldDeploy.ts create mode 100644 packages/core/installMachine/installSteps/stapler/index.ts create mode 100644 packages/core/installMachine/installSteps/supabase/index.ts create mode 100644 packages/core/installMachine/installSteps/tailwind/index.ts create mode 100644 packages/core/installMachine/installSteps/turbo/index.ts create mode 100644 packages/core/installMachine/installSteps/vercel/index.ts diff --git a/.changeset/pink-steaks-push.md b/.changeset/pink-steaks-push.md new file mode 100644 index 0000000..5fe0de7 --- /dev/null +++ b/.changeset/pink-steaks-push.md @@ -0,0 +1,6 @@ +--- +'stplr-core': patch +'stplr': patch +--- + +Add --no-deploy flag workflow diff --git a/.changeset/pre.json b/.changeset/pre.json index 8664c48..3c43652 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -35,6 +35,7 @@ "new-eggs-cheat", "nine-berries-raise", "old-radios-bake", + "pink-steaks-push", "pretty-bags-travel", "rare-beans-divide", "selfish-cheetahs-vanish", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index a051bf2..8995476 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # stplr +## 0.1.0-alpha.43 + +### Patch Changes + +- Add --no-deploy flag workflow + ## 0.1.0-alpha.42 ### Minor Changes diff --git a/packages/cli/README.md b/packages/cli/README.md index afa71f3..75d573b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -107,3 +107,50 @@ This command will guide you through the setup process and create a new project i - [Next.js](https://nextjs.org/) - [Supabase](https://supabase.io/) - [Payload CMS](https://payloadcms.com/) + +## Available Flags + +### `-n, --name ` + +**Description**: Set a specific name for the project being created. If not provided, the CLI will prompt you to enter a name. + +**_Usage:_**: + +```bash +npx stplr -n my-awesome-project +``` + +### `--no-deploy` + +**Description**: Use this flag to set up the project locally without creating a GitHub repository, Supabase project, or deploying to Vercel. This is ideal if you want to scaffold the project and set up these integrations later. + +**What Happens?** + +- The project will be scaffolded in the specified (or current) directory. +- Required accounts (e.g., Supabase, Vercel) will not be linked during setup. +- You can resume the setup later to complete the integration steps. + +**_Usage:_** + +```bash +npx stplr --no-deploy +``` + +**To Resume Setup:** +Simply run `stplr` again in the project directory and select the unfinished project when prompted: + +```bash +npx stplr +``` + +The CLI will detect that the project was not fully set up and guide you through the remaining steps. + +### `--skip-payload` + +**Description**: Skip adding Payload CMS to your project. Use this flag if you do not want Payload CMS to be part of your stack. + +**_Usage_**: + +```bash +npx stplr --skip-payload +``` diff --git a/packages/cli/command-prompts/getProjectNamePrompt.ts b/packages/cli/command-prompts/getProjectNamePrompt.ts new file mode 100644 index 0000000..63a28fa --- /dev/null +++ b/packages/cli/command-prompts/getProjectNamePrompt.ts @@ -0,0 +1,13 @@ +import inquirer from 'inquirer'; + +export const getProjectNamePrompt = async (): Promise => + ( + await inquirer.prompt([ + { + type: 'input', + name: 'name', + message: 'What is your project named?', + default: 'my-stapled-app', + }, + ]) + ).name; diff --git a/packages/cli/command-prompts/index.ts b/packages/cli/command-prompts/index.ts new file mode 100644 index 0000000..10e45f5 --- /dev/null +++ b/packages/cli/command-prompts/index.ts @@ -0,0 +1,4 @@ +export * from './getProjectNamePrompt'; +export * from './overwriteDirectoryPrompt'; +export * from './shouldUsePayloadPrompt'; +export * from './unfinishedProjectsChoicePrompt'; diff --git a/packages/cli/command-prompts/overwriteDirectoryPrompt.ts b/packages/cli/command-prompts/overwriteDirectoryPrompt.ts new file mode 100644 index 0000000..4530cb6 --- /dev/null +++ b/packages/cli/command-prompts/overwriteDirectoryPrompt.ts @@ -0,0 +1,19 @@ +import inquirer from 'inquirer'; + +/** + * Prompts the user to confirm whether they want to overwrite an existing project directory. + * + * @param projectName - The name of the project that already exists. + * @returns A promise that returns object with overwrite boolean value. + * + **/ + +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, + }, + ]); diff --git a/packages/cli/command-prompts/shouldUsePayloadPrompt.ts b/packages/cli/command-prompts/shouldUsePayloadPrompt.ts new file mode 100644 index 0000000..4d3e9e8 --- /dev/null +++ b/packages/cli/command-prompts/shouldUsePayloadPrompt.ts @@ -0,0 +1,19 @@ +import inquirer from 'inquirer'; + +/** + * Prompts the user to confirm whether they want to overwrite an existing project directory. + * + * @param projectName - The name of the project that already exists. + * @returns A promise that returns object with usePayload boolean value. + * + **/ + +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, + }, + ]); diff --git a/packages/cli/command-prompts/unfinishedProjectsChoicePrompt.ts b/packages/cli/command-prompts/unfinishedProjectsChoicePrompt.ts new file mode 100644 index 0000000..1a74cda --- /dev/null +++ b/packages/cli/command-prompts/unfinishedProjectsChoicePrompt.ts @@ -0,0 +1,38 @@ +import inquirer from 'inquirer'; +import { ProjectChoice, UnfinishedProject } from '../utils/findUnfinishedProjects'; + +export type UnfinishedProjectsChoiceAnswers = { + resume: boolean; + unfinishedSelectedProject: UnfinishedProject; +}; + +/** + * Prompts the user to select an unfinished project to resume. + * + * @param unfinishedProjects - An array of unfinished projects. + * @param projectChoices - An array of project choices presented to the user. + * @returns A promise that returns object with resume boolean value and selected project. + * + **/ + +export const unfinishedProjectsChoice = async ( + unfinishedProjects: UnfinishedProject[], + projectChoices: ProjectChoice[], +): Promise => + 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, + }, + { + type: 'list', + name: 'unfinishedSelectedProject', + message: 'Select a project to resume:', + choices: projectChoices, + when: (answers) => answers.resume && unfinishedProjects.length > 1, + }, + ]); diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 94b254c..9d2dbfe 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -2,34 +2,27 @@ import fs from 'fs'; import chalk from 'chalk'; import { Command } from 'commander'; -import gradient from 'gradient-string'; -import inquirer from 'inquirer'; import { createProject } from 'stplr-core'; -import { checkAuthentication } from './utils/checkAuthentication'; -import { checkTools } from './utils/checkTools'; -import { findUnfinishedProjects, UnfinishedProject } from './utils/findUnfinishedProjects'; - -const asciiArt = ` -.&&&% &&&& -.&&&% &&&& -.&&&&&&&&* (&&&&&&&&&&* (&&&.&&&&&&&&&& *&&&# &&&& #&&&&, -.&&&&((((, %&&&&(, .,#&&&&# (&&&&&%(**(%&&&&( *&&&# &&&& (&&&% -.&&&% %&&&. ,&&&% (&&&/ &&&&. *&&&# &&&& #&&&# -.&&&% ,&&&* (&&&. (&&&/ %&&&, *&&&# &&&&&&&&&&. -.&&&% %&&&. *&&&# (&&&/ %&&&, *&&&# &&&&&* *&&&% -.&&&% %&&&&%. ,&&&&&# (&&&/ %&&&, *&&&# &&&& #&&&/ -.&&&% (&&&&&&&&&%* (&&&/ %&&&, *&&&# &&&& .&&&&, -`; - -const displayHeader = () => { - const metalGradient = gradient([ - { color: '#3C3C3C', pos: 0 }, - { color: '#FFFFFF', pos: 1 }, - ]); - - console.log(metalGradient(asciiArt)); - console.log(chalk.bold('\nWelcome to Stapler!\n')); -}; +import { + checkAuthentication, + checkTools, + displayHeader, + findUnfinishedProjects, + getProjectChoices, + UnfinishedProject, +} from './utils'; +import { + getProjectNamePrompt, + overwriteDirectoryPrompt, + shouldUsePayloadPrompt, + unfinishedProjectsChoice, +} from './command-prompts'; + +interface Flags { + deploy?: boolean; + name?: string; + skipPayload?: boolean; +} const program = new Command(); @@ -43,88 +36,68 @@ program displayHeader(); }) .option('-n, --name ', 'Set the name of the project') - .option('--skip-payload', 'Skip adding Payload to the app') - .option('--resume', 'Resume an unfinished project'); + .option( + '--no-deploy', + 'Setup project locally without creating github repository, supabase project and vercel deployment', + ) + .option('--skip-payload', 'Skip adding Payload to the app'); -interface Flags { - name?: string; - skipPayload?: boolean; -} +program.parse(process.argv); const createAction = async (options: Flags) => { + const shouldDeploy = options.deploy as boolean; const currentDir = process.cwd(); - const unfinishedProjects: UnfinishedProject[] = findUnfinishedProjects(currentDir); + let proceedWithNewProject = true; let selectedProject: UnfinishedProject | null = null; - if (unfinishedProjects.length > 0) { - const projectChoices = unfinishedProjects.map((proj) => ({ - name: proj.projectName, - value: proj, - })); - - const resumeAnswers = 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, - }, - { - type: 'list', - name: 'selectedProject', - message: 'Select a project to resume:', - choices: projectChoices, - when: (answers) => answers.resume && unfinishedProjects.length > 1, - }, - ]); + const unfinishedProjects: UnfinishedProject[] = findUnfinishedProjects(currentDir); + + // 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); - if (resumeAnswers.resume) { + const { resume, unfinishedSelectedProject } = await unfinishedProjectsChoice(unfinishedProjects, projectChoices); + + if (resume) { proceedWithNewProject = false; if (unfinishedProjects.length === 1) { selectedProject = unfinishedProjects[0]; } else { - selectedProject = resumeAnswers.selectedProject || null; + selectedProject = unfinishedSelectedProject || null; } } } + // resume selected project if (!proceedWithNewProject && selectedProject) { process.chdir(selectedProject.projectPath); selectedProject.state.options.name = selectedProject.projectName; - await createProject(selectedProject.state.options, selectedProject.projectPath).catch((error) => { - console.error('Error resuming project:', error); - }); + + if (shouldDeploy) { + await checkAuthentication(); + await checkTools(); + } + + await createProject({ ...selectedProject.state.options, shouldDeploy }, selectedProject.projectPath).catch( + (error) => { + console.error('Error resuming project:', error); + }, + ); } else { // Create new project - const projectName = - options.name || - ( - await inquirer.prompt([ - { - type: 'input', - name: 'name', - message: 'What is your project named?', - default: 'my-stapled-app', - }, - ]) - ).name; + options.name && + console.log('You have provided a project name of:', chalk.yellow(options.name), "let's continue..."); + + const projectName = options.name || (await getProjectNamePrompt()); const projectDir = `${currentDir}/${projectName}`; - // Check if the directory already exists - if (fs.existsSync(projectDir)) { - const overwriteAnswer = await inquirer.prompt([ - { - type: 'confirm', - name: 'overwrite', - message: `The directory "${projectName}" already exists. Do you want to overwrite it?`, - default: false, - }, - ]); - - if (!overwriteAnswer.overwrite) { + const directoryExists = fs.existsSync(projectDir); + + if (directoryExists) { + const shouldOverwrite = (await overwriteDirectoryPrompt(projectName)).overwrite; + + if (shouldOverwrite === false) { console.log(chalk.red('Project creation canceled. Project directory already exists.')); return; } @@ -135,21 +108,13 @@ const createAction = async (options: Flags) => { } // Skip Payload if specified by the flag - const payloadAnswer = options.skipPayload - ? { usePayload: false } - : await inquirer.prompt([ - { - type: 'confirm', - name: 'usePayload', - message: 'Would you like to add Payload to your app?', - default: true, - }, - ]); - - const finalOptions = { name: projectName, ...payloadAnswer }; - - await checkAuthentication(); - await checkTools(); + const payloadAnswer = options.skipPayload ? { usePayload: false } : await shouldUsePayloadPrompt(); + + const finalOptions = { name: projectName, shouldDeploy, ...payloadAnswer }; + if (shouldDeploy) { + await checkAuthentication(); + await checkTools(); + } await createProject(finalOptions, projectDir).catch((error) => { console.error(chalk.red('Error creating project:', error)); @@ -162,8 +127,6 @@ program .description( 'CLI tool to bootstrap an app with a variety of integrated steps. This tool guides you through the entire process of initializing, configuring, and deploying a new project.', ) - .option('-n, --name ', 'Set the name of the project') - .option('--skip-payload', 'Skip adding Payload to the app') - .action(createAction); + .action(() => createAction(program.opts())); program.parse(); diff --git a/packages/cli/package.json b/packages/cli/package.json index 4215898..b088715 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "stplr", - "version": "0.1.0-alpha.42", + "version": "0.1.0-alpha.43", "main": "./dist/index.mjs", "bin": { "stplr": "./dist/index.mjs" diff --git a/packages/cli/utils/displayHeader.ts b/packages/cli/utils/displayHeader.ts new file mode 100644 index 0000000..3b04d9a --- /dev/null +++ b/packages/cli/utils/displayHeader.ts @@ -0,0 +1,24 @@ +import gradient from 'gradient-string'; +import chalk from 'chalk'; + +const asciiArt = ` +.&&&% &&&& +.&&&% &&&& +.&&&&&&&&* (&&&&&&&&&&* (&&&.&&&&&&&&&& *&&&# &&&& #&&&&, +.&&&&((((, %&&&&(, .,#&&&&# (&&&&&%(**(%&&&&( *&&&# &&&& (&&&% +.&&&% %&&&. ,&&&% (&&&/ &&&&. *&&&# &&&& #&&&# +.&&&% ,&&&* (&&&. (&&&/ %&&&, *&&&# &&&&&&&&&&. +.&&&% %&&&. *&&&# (&&&/ %&&&, *&&&# &&&&&* *&&&% +.&&&% %&&&&%. ,&&&&&# (&&&/ %&&&, *&&&# &&&& #&&&/ +.&&&% (&&&&&&&&&%* (&&&/ %&&&, *&&&# &&&& .&&&&, +`; + +export const displayHeader = () => { + const metalGradient = gradient([ + { color: '#3C3C3C', pos: 0 }, + { color: '#FFFFFF', pos: 1 }, + ]); + + console.log(metalGradient(asciiArt)); + console.log(chalk.bold('\nWelcome to Stapler!\n')); +}; diff --git a/packages/cli/utils/findUnfinishedProjects.ts b/packages/cli/utils/findUnfinishedProjects.ts index 37ae325..99afdde 100644 --- a/packages/cli/utils/findUnfinishedProjects.ts +++ b/packages/cli/utils/findUnfinishedProjects.ts @@ -65,3 +65,14 @@ export const findUnfinishedProjects = (dir: string): UnfinishedProject[] => { return results; }; + +export interface ProjectChoice { + name: string; + value: UnfinishedProject; +} + +export const getProjectChoices = (unfinishedProjects: UnfinishedProject[]): ProjectChoice[] => + unfinishedProjects.map((proj) => ({ + name: proj.projectName, + value: proj, + })); diff --git a/packages/cli/utils/index.ts b/packages/cli/utils/index.ts new file mode 100644 index 0000000..6cfa092 --- /dev/null +++ b/packages/cli/utils/index.ts @@ -0,0 +1,4 @@ +export * from './checkAuthentication'; +export * from './checkTools'; +export * from './displayHeader'; +export * from './findUnfinishedProjects'; diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 11f7c86..72bd360 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # stplr-core +## 0.2.0-alpha.25 + +### Patch Changes + +- Add --no-deploy flag workflow + ## 0.2.0-alpha.24 ### Patch Changes diff --git a/packages/core/index.ts b/packages/core/index.ts index 485d0d5..8625d71 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -3,9 +3,9 @@ import { initializeRcFile } from './utils/rcFileManager'; import { installMachine } from './installMachine/index'; export const createProject = async (options: ProjectOptions, projectDir: string): Promise => { - const { name, usePayload } = options; + const { name, usePayload, shouldDeploy } = options; - let state: StaplerState = initializeRcFile(projectDir, name, usePayload); + let state: StaplerState = initializeRcFile(projectDir, name, usePayload, shouldDeploy); state.options = options; const context: InstallMachineContext = { diff --git a/packages/core/installMachine/index.ts b/packages/core/installMachine/index.ts index aa089c3..64872c9 100644 --- a/packages/core/installMachine/index.ts +++ b/packages/core/installMachine/index.ts @@ -2,23 +2,21 @@ import { ActorLogic, AnyEventObject, PromiseSnapshot, and, createActor, createMa import { InstallMachineContext, StepsCompleted } from '../types'; import { saveStateToRcFile } from '../utils/rcFileManager'; -import { initializeRepository } from './installSteps/github/install'; -import { pushToGitHub } from './installSteps/github/repositoryManager'; -import { modifyHomepage } from './installSteps/homepage/install'; -import { preparePayload } from './installSteps/payload/install'; -import { prettify } from './installSteps/prettier/prettify'; -import { createDocFiles } from './installSteps/stapler/createDocFiles'; -import { modifyGitignore } from './installSteps/stapler/modifyGitignore'; -import { prepareDrink } from './installSteps/stapler/prepareDrink'; -import { connectSupabaseProject } from './installSteps/supabase/connectProject'; -import { createSupabaseProject } from './installSteps/supabase/createProject'; -import { installSupabase } from './installSteps/supabase/install'; -import { installTailwind } from './installSteps/tailwind/install'; -import { createTurbo } from './installSteps/turbo/install'; -import { chooseVercelTeam } from './installSteps/vercel/chooseTeam'; -import { deployVercelProject } from './installSteps/vercel/deploy'; -import { linkVercelProject } from './installSteps/vercel/link'; -import { updateVercelProjectSettings } from './installSteps/vercel/updateProjectSettings'; +import { initializeRepository, pushToGitHub } from './installSteps/github'; +import { modifyHomepage } from './installSteps/homepage'; +import { preparePayload } from './installSteps/payload'; +import { prettify } from './installSteps/prettier'; +import { createDocFiles, modifyGitignore, prepareDrink } from './installSteps/stapler'; +import { connectSupabaseProject, createSupabaseProject, installSupabase } from './installSteps/supabase'; +import { installTailwind } from './installSteps/tailwind'; +import { createTurbo } from './installSteps/turbo'; +import { + chooseVercelTeam, + deployVercelProject, + linkVercelProject, + updateVercelProjectSettings, +} from './installSteps/vercel'; +import { shouldDeploy } from './installSteps/shouldDeploy'; const isStepCompleted = (stepName: keyof StepsCompleted) => { return ({ context }: { context: InstallMachineContext; event: AnyEventObject }) => { @@ -174,13 +172,29 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { always: [ { guard: isStepCompleted('prettifyCode'), - target: 'initializeRepository', + target: 'shouldDeploy', }, ], invoke: { input: ({ context }) => context, src: 'prettifyCodeActor', - onDone: 'initializeRepository', + onDone: 'shouldDeploy', + onError: 'failed', + }, + }, + shouldDeploy: { + invoke: { + input: ({ context }) => context, + src: 'shouldDeployActor', + onDone: [ + { + guard: 'shouldDeploy', + target: 'initializeRepository', + }, + { + target: 'prepareDrink', + }, + ], onError: 'failed', }, }, @@ -327,6 +341,9 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { shouldInstallPayload: ({ context }) => { return context.stateData.options.usePayload; }, + shouldDeploy: ({ context }) => { + return context.stateData.options.shouldDeploy; + }, }, actors: { createTurboActor: createStepMachine( @@ -428,6 +445,17 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { } }), ), + shouldDeployActor: createStepMachine( + fromPromise(async ({ input }) => { + try { + const shouldContinue = await shouldDeploy(input.stateData.options.shouldDeploy); + input.stateData.options.shouldDeploy = shouldContinue; + } catch (error) { + console.error('Error in shouldDeployActor:', error); + throw error; + } + }), + ), initializeRepositoryActor: createStepMachine( fromPromise(async ({ input }) => { try { @@ -531,9 +559,16 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { prepareDrinkActor: createStepMachine( fromPromise(async ({ input }) => { try { - const { projectName, prettyDeploymentUrl } = input.stateData; - prepareDrink(projectName, prettyDeploymentUrl); - input.stateData.stepsCompleted.prepareDrink = true; + const { + projectName, + prettyDeploymentUrl, + options: { shouldDeploy }, + } = input.stateData; + prepareDrink(projectName, prettyDeploymentUrl, shouldDeploy); + if (shouldDeploy) { + input.stateData.stepsCompleted.shouldDeploy = true; + input.stateData.stepsCompleted.prepareDrink = true; + } saveStateToRcFile(input.stateData, input.projectDir); } catch (error) { console.error('Error in prepareDrinkActor:', error); diff --git a/packages/core/installMachine/installSteps/github/index.ts b/packages/core/installMachine/installSteps/github/index.ts new file mode 100644 index 0000000..a9c6939 --- /dev/null +++ b/packages/core/installMachine/installSteps/github/index.ts @@ -0,0 +1,6 @@ +export * from './checkGitHubCLI'; +export * from './ensureGitHubAuthentication'; +export * from './fetchOrganizations'; +export * from './ghInstaller'; +export * from './install'; +export * from './repositoryManager'; diff --git a/packages/core/installMachine/installSteps/homepage/index.ts b/packages/core/installMachine/installSteps/homepage/index.ts new file mode 100644 index 0000000..b3910c7 --- /dev/null +++ b/packages/core/installMachine/installSteps/homepage/index.ts @@ -0,0 +1 @@ +export * from './install'; diff --git a/packages/core/installMachine/installSteps/payload/index.ts b/packages/core/installMachine/installSteps/payload/index.ts new file mode 100644 index 0000000..4dba725 --- /dev/null +++ b/packages/core/installMachine/installSteps/payload/index.ts @@ -0,0 +1,7 @@ +export * from './install'; +export * from './moveFilesToAppDir'; +export * from './preparePayloadConfig'; +export * from './prepareTsConfig'; +export * from './removeTurboFlag'; +export * from './runInstallCommand'; +export * from './updatePackages'; diff --git a/packages/core/installMachine/installSteps/prettier/index.ts b/packages/core/installMachine/installSteps/prettier/index.ts new file mode 100644 index 0000000..756b815 --- /dev/null +++ b/packages/core/installMachine/installSteps/prettier/index.ts @@ -0,0 +1 @@ +export * from './prettify'; diff --git a/packages/core/installMachine/installSteps/shouldDeploy/index.ts b/packages/core/installMachine/installSteps/shouldDeploy/index.ts new file mode 100644 index 0000000..e1b654e --- /dev/null +++ b/packages/core/installMachine/installSteps/shouldDeploy/index.ts @@ -0,0 +1 @@ +export * from './shouldDeploy'; diff --git a/packages/core/installMachine/installSteps/shouldDeploy/shouldDeploy.ts b/packages/core/installMachine/installSteps/shouldDeploy/shouldDeploy.ts new file mode 100644 index 0000000..2f6065d --- /dev/null +++ b/packages/core/installMachine/installSteps/shouldDeploy/shouldDeploy.ts @@ -0,0 +1,33 @@ +import inquirer from 'inquirer'; +import { logger } from 'stplr-utils'; + +export const shouldDeploy = async (shouldContinue: boolean): Promise => { + return await logger.withSpinner('deployment', 'Deciding next steps...', async (spinner) => { + if (!shouldContinue) { + spinner.succeed('Local deployment completed'); + return false; + } + + try { + spinner.stop(); + const answers = (await inquirer.prompt([ + { + type: 'confirm', + name: 'continue', + message: + 'Local installation completed. Would you like to continue with remote setup (GitHub, Supabase, Vercel)?', + default: true, + }, + ])) as { continue: boolean }; + spinner.start(); + const spinnerMessage = answers.continue ? 'Continuing with remote setup...' : 'Local deployment completed'; + spinner.succeed(spinnerMessage); + + return answers.continue; + } catch (error) { + spinner.fail('Local deployment failed'); + console.error('Error during local deployment:', error); + return false; + } + }); +}; diff --git a/packages/core/installMachine/installSteps/stapler/index.ts b/packages/core/installMachine/installSteps/stapler/index.ts new file mode 100644 index 0000000..a3f4401 --- /dev/null +++ b/packages/core/installMachine/installSteps/stapler/index.ts @@ -0,0 +1,4 @@ +export * from './createDocFiles'; +export * from './initializeStapler'; +export * from './modifyGitignore'; +export * from './prepareDrink'; diff --git a/packages/core/installMachine/installSteps/stapler/prepareDrink.ts b/packages/core/installMachine/installSteps/stapler/prepareDrink.ts index d01d060..d4af8b9 100644 --- a/packages/core/installMachine/installSteps/stapler/prepareDrink.ts +++ b/packages/core/installMachine/installSteps/stapler/prepareDrink.ts @@ -1,20 +1,27 @@ import chalk from 'chalk'; import { delay } from '../../../utils/delay'; -const getMessages = (name: string, prettyDeploymentUrl: string) => { - return [ +const getMessages = (name: string, prettyDeploymentUrl: string, shouldDeploy: boolean) => { + const messages = [ '🍸 Filling a high ball glass with ice...', '🍸 Adding gin and lime juice...', `🍸 Topping with ${chalk.blue('Tonik')}...`, '🍸 Garnishing with lime wedge...', `🍸 ${chalk.green(`Your Stapled ${name} is ready!`)}`, `🍸 Ready to explore? Jump into your project with: ${chalk.cyan(`cd ${name} && pnpm dev`)}`, - `🍸 Prefer to see it online? Check it out here: ${chalk.cyan(prettyDeploymentUrl)}`, ]; + + if (shouldDeploy) { + messages.push(`🍸 Prefer to see it online? Check it out here: ${chalk.cyan(prettyDeploymentUrl)}`); + } else { + messages.push('🍸 Want to deploy your project? Run `stplr` within your project directory.'); + } + + return messages; }; -export const prepareDrink = async (name: string, prettyDeploymentUrl: string) => { - const messages = getMessages(name, prettyDeploymentUrl); +export const prepareDrink = async (name: string, prettyDeploymentUrl: string, shouldDeploy: boolean) => { + const messages = getMessages(name, prettyDeploymentUrl, shouldDeploy); for (const message of messages) { console.log(message); diff --git a/packages/core/installMachine/installSteps/supabase/index.ts b/packages/core/installMachine/installSteps/supabase/index.ts new file mode 100644 index 0000000..d9ef779 --- /dev/null +++ b/packages/core/installMachine/installSteps/supabase/index.ts @@ -0,0 +1,9 @@ +export * from './addTemplateFiles'; +export * from './authenticateSupabase'; +export * from './checkSupabaseCLI'; +export * from './connectProject'; +export * from './createEnvFile'; +export * from './createProject'; +export * from './initializeSupabaseProject'; +export * from './install'; +export * from './installDependencies'; diff --git a/packages/core/installMachine/installSteps/tailwind/index.ts b/packages/core/installMachine/installSteps/tailwind/index.ts new file mode 100644 index 0000000..b3910c7 --- /dev/null +++ b/packages/core/installMachine/installSteps/tailwind/index.ts @@ -0,0 +1 @@ +export * from './install'; diff --git a/packages/core/installMachine/installSteps/turbo/index.ts b/packages/core/installMachine/installSteps/turbo/index.ts new file mode 100644 index 0000000..b3910c7 --- /dev/null +++ b/packages/core/installMachine/installSteps/turbo/index.ts @@ -0,0 +1 @@ +export * from './install'; diff --git a/packages/core/installMachine/installSteps/vercel/index.ts b/packages/core/installMachine/installSteps/vercel/index.ts new file mode 100644 index 0000000..46db93d --- /dev/null +++ b/packages/core/installMachine/installSteps/vercel/index.ts @@ -0,0 +1,6 @@ +export * from './authenticateVercel'; +export * from './checkVercelCLI'; +export * from './chooseTeam'; +export * from './deploy'; +export * from './link'; +export * from './updateProjectSettings'; diff --git a/packages/core/package.json b/packages/core/package.json index f97e578..e2658d8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "stplr-core", - "version": "0.2.0-alpha.24", + "version": "0.2.0-alpha.25", "private": true, "main": "./dist/index.js", "scripts": { diff --git a/packages/core/types.ts b/packages/core/types.ts index cd485bc..c66d891 100644 --- a/packages/core/types.ts +++ b/packages/core/types.ts @@ -1,6 +1,7 @@ export interface ProjectOptions { name: string; usePayload: boolean; + shouldDeploy: boolean; } export interface StepsCompleted { @@ -12,6 +13,7 @@ export interface StepsCompleted { installPayload: boolean; createDocFiles: boolean; prettifyCode: boolean; + shouldDeploy: boolean; initializeRepository: boolean; pushToGitHub: boolean; createSupabaseProject: boolean; diff --git a/packages/core/utils/rcFileManager/index.ts b/packages/core/utils/rcFileManager/index.ts index 2ffc504..37a9229 100644 --- a/packages/core/utils/rcFileManager/index.ts +++ b/packages/core/utils/rcFileManager/index.ts @@ -4,7 +4,12 @@ import { StaplerState } from '../../types'; const RC_FILE_NAME = '.staplerrc'; -export const initializeRcFile = (projectDir: string, name: string, usePayload: boolean): StaplerState => { +export const initializeRcFile = ( + projectDir: string, + name: string, + usePayload: boolean, + shouldDeploy: boolean, +): StaplerState => { const rcFilePath = path.join(projectDir, RC_FILE_NAME); if (fs.existsSync(rcFilePath)) { const data = fs.readFileSync(rcFilePath, 'utf-8'); @@ -23,6 +28,7 @@ export const initializeRcFile = (projectDir: string, name: string, usePayload: b installPayload: false, createDocFiles: false, prettifyCode: false, + shouldDeploy: false, prepareDrink: false, initializeRepository: false, pushToGitHub: false, @@ -36,6 +42,7 @@ export const initializeRcFile = (projectDir: string, name: string, usePayload: b options: { name: name, usePayload: usePayload, + shouldDeploy: shouldDeploy, }, githubCandidateName: name, selectedAccount: '', diff --git a/packages/utils/logger.ts b/packages/utils/logger.ts index cf10ba8..e69cce9 100644 --- a/packages/utils/logger.ts +++ b/packages/utils/logger.ts @@ -10,6 +10,7 @@ type Name = | 'payload' | 'github' | 'prettier' + | 'deployment' | 'vercel' | 'docker' | 'postgres' @@ -57,6 +58,11 @@ const names: NameProps[] = [ prefix: 'Prettier', colors: ['#F11D28', '#FFA12C'], }, + { + name: 'deployment', + prefix: 'Deployment', + colors: ['#c83488', '#FAD400'], + }, { name: 'vercel', prefix: 'Vercel',