diff --git a/src/cli/index.ts b/src/cli/index.ts index 6d2bbfa..d0d17e3 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -11,10 +11,15 @@ export default class Cli { instructions: string[] = []; projects: string[] = []; args: CliInput; + projectsWithSubfolders: string[] = []; async initialize() { // Grab projects - this.projects = await getProjects(); + const result = await getProjects(); + + this.projects = result?.[0] ?? [] + this.projectsWithSubfolders = result?.[1] ?? [] + // Parse CLI arguments const cliArgs = Command.parameters(parameters).parse(); // Massage & apply the data diff --git a/src/cli/prompts.ts b/src/cli/prompts.ts index 338dd42..4f89dde 100644 --- a/src/cli/prompts.ts +++ b/src/cli/prompts.ts @@ -115,6 +115,7 @@ const getProjectName = async (defaultValue = ""): Promise => { try { validate.directoryName(answer); } catch (e) { + console.log("\n✗ " + chalk.red(e.message)) return false; } return true; @@ -133,6 +134,7 @@ const getProjectDirectory = async (): Promise => { try { validate.directory(answer); } catch (e) { + console.log("\n✗ " + chalk.red(e.message)) return false; } return true; diff --git a/src/cli/validation.ts b/src/cli/validation.ts index d6cab5d..9a7809c 100644 --- a/src/cli/validation.ts +++ b/src/cli/validation.ts @@ -11,7 +11,7 @@ export default { rootDir(path: string) { if (!EXAMPLES_DIR_ACCEPT.includes(path)) { throw new Error( - "Invalid template. Please choose a template from the `javascript` or `typescript` directories in the prisma/prisma-examples repository.", + "Invalid template. Please choose a template from the `javascript`, `typescript`, `accelerate` or `pulse` directories in the prisma/prisma-examples repository.", ); } }, @@ -21,7 +21,11 @@ export default { } if (!fs.existsSync(path)) { - throw new Error(`Unable to reach a directory at ${path}`); + try { + fs.mkdirSync(path, { recursive: true }); + } catch { + throw new Error(`Unable to reach a directory at ${path}`); + } } }, project(projects: string[], project: string) { diff --git a/src/helpers/getProjects.ts b/src/helpers/getProjects.ts index 8c3e98c..791d239 100644 --- a/src/helpers/getProjects.ts +++ b/src/helpers/getProjects.ts @@ -37,5 +37,19 @@ export default async function getProjects() { } } - return Object.keys(mergedData).sort(); + const projectsWithSubfolders:string[] = [] + + const paths = Object.keys(mergedData).map(item => { + const folders = item.split('/'); + + if(folders.length >= 3) { + projectsWithSubfolders.push(folders[1]) + } + + return folders.length >= 3 ? folders.slice(0, -1).join('/') : item; + }); + + const uniquePaths = Array.from(new Set(paths)).sort(); + + return [uniquePaths, projectsWithSubfolders]; } diff --git a/src/helpers/installPackages.ts b/src/helpers/installPackages.ts index b759de4..b572a62 100644 --- a/src/helpers/installPackages.ts +++ b/src/helpers/installPackages.ts @@ -11,7 +11,7 @@ export default async function installPackages( spinner.start( `Running ${chalk.greenBright( `\`${manager} install\``, - )}. This may take a bit...`, + )} in ${execDir}. This may take a bit...`, ); if (!manager) { @@ -23,7 +23,7 @@ export default async function installPackages( await execa(`${manager.toLowerCase()} install`, { cwd: `${process.cwd()}/${execDir}`, }); - spinner.succeed(`Installed packages.`); + spinner.succeed(`Installed packages in ${execDir}.`); } catch (e) { spinner.stopAndPersist(); throw Error( diff --git a/src/index.ts b/src/index.ts index 001a899..8b456b6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,10 +10,45 @@ const main = async () => { const cli = new CLI(); await cli.initialize(); const input = await cli.collect(); + + const isProjectWithSubdirectory = cli.projectsWithSubfolders.reduce( + (prev, curr) => { + return prev || cli.args.template.includes(curr); + }, + false, + ); + await download(input); if (input.install) { - await installPackages(input.pkgMgr, `${input.path}/${input.name}`); + if (!isProjectWithSubdirectory) { + await installPackages(input.pkgMgr, `${input.path}/${input.name}`); + } else { + if (cli.args.template.includes("fullstack-simple-chat")) { + await installPackages( + input.pkgMgr, + `${input.path}/${input.name}/client`, + ); + await installPackages( + input.pkgMgr, + `${input.path}/${input.name}/server`, + ); + } + + if ( + cli.args.template.includes("product-search-with-typesense") || + cli.args.template.includes("rest-nextjs-express") + ) { + await installPackages( + input.pkgMgr, + `${input.path}/${input.name}/frontend`, + ); + await installPackages( + input.pkgMgr, + `${input.path}/${input.name}/backend`, + ); + } + } } if (input.vscode) { @@ -37,22 +72,22 @@ const main = async () => { chalk.hex("#4C51BF")(`npx prisma migrate dev`), ); - if (input.name.includes("accelerate") || input.name.includes("pulse")) { - logger.success(` + logger.success(` ${chalk.bold(`The project is good to go! Next steps:`)} ${"Please follow the instructions in the project README to run it:"} ${chalk.bold(`https://github.com/prisma/prisma-examples/tree/latest/${input.template}`)} - `); - } else { - logger.success(` - ${chalk.bold(`The project is good to go! Next steps:`)} - ${cli.instructions.join("")} - For more information about this project, visit: - ${chalk.gray.underline( - `https://github.com/prisma/prisma-examples/tree/latest/${input.template}`, - )} - `); - } + `) + + // } else { + // logger.success(` + // ${chalk.bold(`The project is good to go! Next steps:`)} + // ${cli.instructions.join("")} + // For more information about this project, visit: + // ${chalk.gray.underline( + // `https://github.com/prisma/prisma-examples/tree/latest/${input.template}`, + // )} + // `); + // } logger.success( `If you have any feedback about this specific template, we want to hear it!\nSubmit any feedback here: ${chalk.gray.underline( "https://pris.ly/prisma-examples-feedback", diff --git a/test/cli/input-collector.test.ts b/test/cli/input-collector.test.ts index bf50eba..6ac8c34 100644 --- a/test/cli/input-collector.test.ts +++ b/test/cli/input-collector.test.ts @@ -77,7 +77,7 @@ describe("Input Collector", () => { expect(() => MockCLI.validateUserInput()).toThrow(); }); it("Should return an error if a directory name is provided and is invalid", () => { - MockCLI.args.path = "directory"; + MockCLI.args.path = "directory/"; expect(() => MockCLI.validateUserInput()).toThrow(); }); afterEach(() => { diff --git a/test/helpers/getProjects.test.ts b/test/helpers/getProjects.test.ts index 19e00aa..3e9a1f8 100644 --- a/test/helpers/getProjects.test.ts +++ b/test/helpers/getProjects.test.ts @@ -32,7 +32,7 @@ describe("Get Projects", () => { }); const data = await getProjects(); - expect(data).toStrictEqual([]); + expect(data[0]).toStrictEqual([]); }); it("Should ignore directories that do not have a package.json", async () => { vi.spyOn(fetch as any, "default").mockResolvedValue({ @@ -52,7 +52,7 @@ describe("Get Projects", () => { }); const data = await getProjects(); - expect(data).toStrictEqual([]); + expect(data[0]).toStrictEqual([]); }); it("Should return an array with a file path to a valid project", async () => { @@ -73,8 +73,8 @@ describe("Get Projects", () => { }); const data = await getProjects(); - expect(data.length).toBe(1); - expect(data).toContain("typescript"); + expect(data[0].length).toBe(1); + expect(data[0]).toContain("typescript"); }); it('Should work as expected with a "tree" type', async () => { @@ -95,8 +95,8 @@ describe("Get Projects", () => { }); const data = await getProjects(); - expect(data.length).toBe(1); - expect(data).toContain("javascript"); + expect(data[0].length).toBe(1); + expect(data[0]).toContain("javascript"); }); it("Should return multiple valid projects", async () => { @@ -124,10 +124,10 @@ describe("Get Projects", () => { }), }); const data = await getProjects(); - - expect(data.length).toBe(2); - expect(data).toContain("javascript"); - expect(data).toContain("typescript"); + console.log(data[0]) + expect(data[0].length).toBe(2); + expect(data[0]).toContain("typescript"); + expect(data[0]).toContain("javascript"); }); it("Should return valid projects and filter bad projects", async () => { @@ -156,7 +156,7 @@ describe("Get Projects", () => { }); const data = await getProjects(); - expect(data.length).toBe(1); - expect(data).toContain("typescript"); + expect(data[0].length).toBe(1); + expect(data[0]).toContain("typescript"); }); });