From fc4a155cbd280d7ea85c698cf3bb2e87d6996c35 Mon Sep 17 00:00:00 2001 From: Nazar Kornienko Date: Sat, 11 Jan 2025 00:02:54 +0100 Subject: [PATCH] temporarily combine config code in one file --- build.optim.ts | 2 +- build.publish.ts | 2 +- bun.lockb | Bin 503499 -> 503949 bytes eslint.config.js | 12 + jsr.jsonc | 2 +- package.json | 3 +- publish.ts | 2 +- src/app/app-impl.ts | 15 +- src/app/app-mod.ts | 12 +- src/app/db/client.ts | 2 +- src/app/db/config.ts | 2 +- src/app/menu/create-project/cp-impl.ts | 23 +- src/app/menu/create-project/cp-mod.ts | 8 +- .../cli-menu-items/getMainMenuOptions.ts | 2 +- .../cli-menu-items/getProjectMenuOptions.ts | 2 +- .../installAnyGitRepoProject.ts | 15 +- .../cli-main-modules/configs/appts.ts | 2 +- .../cli-main-modules/configs/biome.ts | 5 +- .../cli-main-modules/configs/env.ts | 2 +- .../cli-main-modules/configs/envjs.ts | 2 +- .../cli-main-modules/configs/eslint.ts | 2 +- .../configs/generateDefaultRulesForProject.ts | 86 - .../cli-main-modules/configs/knip.ts | 5 +- .../configs/miscellaneousConfigHelpers.ts | 312 ---- .../cli-main-modules/configs/nextjs.ts | 5 +- .../configs/parseCodeStyleFromConfigs.ts | 51 - .../cli-main-modules/configs/putout.ts | 5 +- .../configs/reliverseDefaultConfig.ts | 70 - .../configs/reliverseFileOps.ts | 311 ---- .../configs/reliverseReadWrite.ts | 112 -- .../configs/reliverseSchema.ts | 93 -- .../configs/validateAndInsertMissingKeys.ts | 40 - .../detections/detectReliverseProjects.ts | 92 -- .../detections/detectedProjectsMenu.ts | 34 +- .../downloads/downloadGitRepo.ts | 5 +- .../downloads/downloadI18nFiles.ts | 2 +- .../drizzle/manageDrizzleSchema.ts | 2 +- .../drizzle/manageDrizzleSchemaUtils.ts | 6 +- .../handlers/cloneAndCopyFiles.ts | 5 +- .../handlers/codemods/convertCjsToEsm.ts | 2 +- .../handlers/codemods/convertDatabase.ts | 2 +- .../handlers/codemods/convertDefinitions.ts | 2 +- .../handlers/codemods/convertImportStyle.ts | 2 +- .../handlers/codemods/convertJsToTs.ts | 2 +- .../handlers/codemods/convertQuoteStyle.ts | 2 +- .../handlers/codemods/convertRuntime.ts | 2 +- .../handlers/codemods/convertTailwind.ts | 2 +- .../handlers/codemods/convertToMonorepo.ts | 2 +- .../handlers/codemods/removeComments.ts | 2 +- .../handlers/codemods/replaceImportSymbol.ts | 2 +- .../handlers/codemods/replaceWithModern.ts | 2 +- .../cli-main-modules/handlers/configure.ts | 7 +- .../handlers/generateProjectConfigs.ts | 28 +- .../handlers/handleCleanup.ts | 35 +- .../handlers/handleCodemods.ts | 75 +- .../handlers/handleConfigEdits.ts | 7 +- .../handlers/handleIntegrations.ts | 8 +- .../cli-main-modules/handlers/i18nMove.ts | 2 +- .../handlers/promptPackageJsonScripts.ts | 5 +- .../handlers/reliverseConfig.ts | 186 --- .../handlers/replaceStringsInFiles.ts | 2 +- .../handlers/revalidateReliverseJson.ts | 178 -- .../cli-main-modules/handlers/shadcn.ts | 4 +- .../cli-main-modules/handlers/terminal.ts | 2 +- .../cli-main-modules/handlers/validate.ts | 2 +- .../integrations/integrations.ts | 2 +- .../integrations/integrationsIntegrConfig.ts | 2 +- .../modules/askCodemodUserCodebase.ts | 5 +- .../cli-main-modules/modules/askGithubName.ts | 4 +- .../modules/askSummaryConfirmation.ts | 2 +- .../modules/askToResolveProjectConflicts.ts | 2 +- .../cli-main-modules/modules/askUserName.ts | 4 +- .../cli-main-modules/modules/askVercelName.ts | 2 +- .../modules/showRelivatorFeatEditor.ts | 2 +- .../modules/showStartEndPrompt.ts | 2 +- .../modules/showUpdateCloneMenu.ts | 7 +- .../compose-env-file/helpers/env-manager.ts | 2 +- .../compose-env-file/helpers/env.ts | 2 +- .../compose-env-file/helpers/file.ts | 2 +- .../cp-modules/compose-env-file/mod.ts | 2 +- .../cp-modules/git-deploy-prompts/deploy.ts | 15 +- .../cp-modules/git-deploy-prompts/git.ts | 2 +- .../cp-modules/git-deploy-prompts/github.ts | 6 +- .../helpers/handlePkgJsonScripts.ts | 2 +- .../cp-modules/git-deploy-prompts/mod.ts | 15 +- .../git-deploy-prompts/octokit-instance.ts | 2 +- .../git-deploy-prompts/utils-git-github.ts | 28 +- .../git-deploy-prompts/vercel/vercel-api.ts | 2 +- .../vercel/vercel-config.ts | 2 +- .../vercel/vercel-deploy.ts | 2 +- .../vercel/vercel-domain.ts | 2 +- .../git-deploy-prompts/vercel/vercel-env.ts | 2 +- .../git-deploy-prompts/vercel/vercel-mod.ts | 6 +- .../helpers/createProject.ts | 9 +- .../use-composer-mode/helpers/git.ts | 2 +- .../helpers/installDependencies.ts | 5 +- .../helpers/installPackages.ts | 6 +- .../use-composer-mode/helpers/logNextSteps.ts | 6 +- .../helpers/scaffoldProject.ts | 6 +- .../helpers/selectBoilerplate.ts | 3 +- .../cp-modules/use-composer-mode/impl.ts | 2 +- .../installers/dbContainer.ts | 5 +- .../use-composer-mode/installers/drizzle.ts | 4 +- .../use-composer-mode/installers/envVars.ts | 6 +- .../use-composer-mode/installers/nextAuth.ts | 4 +- .../use-composer-mode/installers/prisma.ts | 5 +- .../use-composer-mode/installers/tailwind.ts | 5 +- .../use-composer-mode/installers/trpc.ts | 5 +- .../extras/src/app/_components/post-tw.tsx | 2 +- .../extras/src/app/_components/post.tsx | 4 +- .../src/app/api/auth/[...nextauth]/route.ts | 2 +- .../extras/src/app/layout/with-trpc-tw.tsx | 2 +- .../extras/src/pages/_app/with-auth-trpc.tsx | 3 +- .../extras/src/pages/_app/with-trpc-tw.tsx | 3 +- .../extras/src/pages/_app/with-trpc.tsx | 3 +- .../src/pages/index/with-auth-trpc-tw.tsx | 2 +- .../extras/src/server/api/routers/post.ts | 2 +- .../template/extras/src/trpc/react.tsx | 3 +- .../template/extras/src/trpc/server.ts | 5 +- .../template/extras/src/utils/api.ts | 2 +- .../utils/addPackageDependency.ts | 4 +- ...eliverseCliVersion.ts => getCliVersion.ts} | 2 +- .../utils/renderVersionWarning.ts | 6 +- src/app/menu/menu-mod.ts | 71 +- src/args/config/mod.ts | 273 ++- src/args/help/mod.ts | 2 +- src/args/login/impl.ts | 7 +- src/args/login/mod.ts | 4 +- src/args/logout/impl.ts | 2 +- src/args/logout/mod.ts | 2 +- src/args/memory/mod.ts | 4 +- src/args/studio/mod.ts | 2 +- src/dev.ts | 14 +- src/main.ts | 4 +- src/types.ts | 193 +-- src/utils/configHandler.ts | 113 ++ .../logger.ts => utils/loggerRelinka.ts} | 0 src/utils/reliverseConfig.ts | 1468 +++++++++++++++++ .../app-utils.ts => utils/reliverseMemory.ts} | 2 +- 139 files changed, 2108 insertions(+), 2217 deletions(-) delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/generateDefaultRulesForProject.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/miscellaneousConfigHelpers.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/parseCodeStyleFromConfigs.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseDefaultConfig.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseFileOps.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseReadWrite.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseSchema.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/configs/validateAndInsertMissingKeys.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectReliverseProjects.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/handlers/reliverseConfig.ts delete mode 100644 src/app/menu/create-project/cp-modules/cli-main-modules/handlers/revalidateReliverseJson.ts rename src/app/menu/create-project/cp-modules/use-composer-mode/utils/{getReliverseCliVersion.ts => getCliVersion.ts} (90%) create mode 100644 src/utils/configHandler.ts rename src/{app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.ts => utils/loggerRelinka.ts} (100%) create mode 100644 src/utils/reliverseConfig.ts rename src/{app/app-utils.ts => utils/reliverseMemory.ts} (98%) diff --git a/build.optim.ts b/build.optim.ts index e8ee5832..ec1cfe6b 100644 --- a/build.optim.ts +++ b/build.optim.ts @@ -3,7 +3,7 @@ import { globby } from "globby"; import path from "pathe"; import { fileURLToPath } from "url"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Parse command-line arguments to check for '--jsr' flag const args: string[] = process.argv.slice(2); diff --git a/build.publish.ts b/build.publish.ts index cab8c1b1..b3e1f7f9 100644 --- a/build.publish.ts +++ b/build.publish.ts @@ -7,7 +7,7 @@ import { globby } from "globby"; import mri from "mri"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; function showHelp() { relinka( diff --git a/bun.lockb b/bun.lockb index 6ba1bfe3054dd71e2c11d9e156ded376c99ba1fb..12368824fcec67085a4e46f59466eb8d0ed690d9 100644 GIT binary patch delta 76991 zcmeFa2Y40L`u07OWJ3l-Ksrbl0W~zyBm~%i6hTE0q_>a+5=bKj2ojTssGz7Y;^r-^46Zj9S81c=;-s) z`l+2OF8d@QG|aC<9jCG+y|{F7spDKmKuhcc;9+2KNoG=3{!(WWc603R4IHNtxCr#X z1(`WT%B2N%GsVx!Tbc#a5c?49nuy~l|F1#Ce_!_DD6gS30mqWDGW=VjRMB%55Ef}(VOJI+N-4gZ?ORiKJn zKwZ^qGeKov0?$un4!>ld4k|n&J3V(X`B=N6ats0Lo;WDRb_834T^y&PG66+y_k1Ul(N<6u2tXoKt^k$bbdac(OCia- zaws-+tNe^#8j!NCCjDoiyy`iyGnj4p1KmveZ$VWozpOCNar`IZ+gH>u6r$cb!gOUP zDoV2|v+@cS<>cnfe;HokJ3+NMKRv&oB)!NH=43j~7j`I1^9yrJ7v|(I>S?Oh1yo

a6~dP&@`U-$rAa2iNCMQt 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 { const isDev = args.dev; + await showStartPrompt(isDev); const cwd = getCurrentWorkingDirectory(); const memory = await getReliverseMemory(); - const config = await getReliverseConfig(cwd); + + // TODO: fix reliverse config and enable it back + // const config = await getReliverseConfig(cwd); + await authCheck(isDev, memory, useLocalhost); - await app({ cwd, isDev, config, memory }); + await app({ cwd, isDev, config: {}, memory }); + process.exit(0); }, }); diff --git a/src/app/db/client.ts b/src/app/db/client.ts index 1ab0e363..01e7e1cb 100644 --- a/src/app/db/client.ts +++ b/src/app/db/client.ts @@ -4,7 +4,7 @@ import fs from "fs-extra"; import os from "os"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Use .reliverse directory in user's home directory const homeDir = os.homedir(); diff --git a/src/app/db/config.ts b/src/app/db/config.ts index 118b1ee4..cafa6841 100644 --- a/src/app/db/config.ts +++ b/src/app/db/config.ts @@ -8,7 +8,7 @@ import { eq } from "drizzle-orm"; import type { ConfigKey } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { db } from "./client.js"; import { configKeysTable } from "./schema.js"; diff --git a/src/app/menu/create-project/cp-impl.ts b/src/app/menu/create-project/cp-impl.ts index 5d11fb6d..96572db5 100644 --- a/src/app/menu/create-project/cp-impl.ts +++ b/src/app/menu/create-project/cp-impl.ts @@ -19,20 +19,20 @@ import pc from "picocolors"; import type { Behavior, DeploymentService, - ReliverseConfig, ReliverseMemory, TemplateOption, } from "~/types.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; import { setupI18nFiles } from "~/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadI18nFiles.js"; import { extractRepoInfo } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/extractRepoInfo.js"; import { isVSCodeInstalled } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/isAppInstalled.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { promptPackageJsonScripts } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/promptPackageJsonScripts.js"; import { replaceStringsInFiles } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/replaceStringsInFiles.js"; import { askProjectName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askProjectName.js"; import { askUserName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askUserName.js"; import { promptGitDeploy } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/mod.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export type PackageJson = { name?: string; @@ -64,18 +64,18 @@ export async function initializeProjectConfig( shouldUseDataFromConfig: boolean, ): Promise { const frontendUsername = - shouldUseDataFromConfig && config?.experimental?.projectAuthor - ? config.experimental.projectAuthor + shouldUseDataFromConfig && config?.projectAuthor + ? config.projectAuthor : ((await askUserName(memory)) ?? ""); const projectName = - shouldUseDataFromConfig && config?.experimental?.projectTemplate - ? path.basename(config.experimental.projectTemplate) + shouldUseDataFromConfig && config?.projectTemplate + ? path.basename(config.projectTemplate) : ((await askProjectName()) ?? ""); const primaryDomain = - shouldUseDataFromConfig && config?.experimental?.projectDomain - ? config.experimental.projectDomain + shouldUseDataFromConfig && config?.projectDomain + ? config.projectDomain : `${projectName}.vercel.app`; return { frontendUsername, projectName, primaryDomain }; @@ -129,9 +129,8 @@ export async function setupI18nSupport( shouldUseDataFromConfig: boolean, ) { const i18nShouldBeEnabled = - shouldUseDataFromConfig && - config?.experimental?.features?.i18n !== undefined - ? config.experimental.features.i18n + shouldUseDataFromConfig && config?.features?.i18n !== undefined + ? config.features.i18n : await confirmPrompt({ title: "Do you want to enable i18n (internationalization) for this project?", @@ -157,7 +156,7 @@ export async function handleDependencies( projectPath: string, config: ReliverseConfig, ) { - const depsBehavior: Behavior = config?.experimental?.depsBehavior ?? "prompt"; + const depsBehavior: Behavior = config?.depsBehavior ?? "prompt"; const shouldInstallDeps = await determineShouldInstallDeps(depsBehavior); let shouldRunDbPush = false; diff --git a/src/app/menu/create-project/cp-mod.ts b/src/app/menu/create-project/cp-mod.ts index 4198fa44..0c12b70a 100644 --- a/src/app/menu/create-project/cp-mod.ts +++ b/src/app/menu/create-project/cp-mod.ts @@ -4,8 +4,8 @@ import { generateProjectConfigs, updateProjectConfig, } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/generateProjectConfigs.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { composeEnvFile } from "~/app/menu/create-project/cp-modules/compose-env-file/mod.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { initializeProjectConfig, @@ -32,8 +32,7 @@ export async function createWebProject({ relinka("info", message); // Check if we should use data from the config - const shouldUseDataFromConfig = - config?.experimental?.skipPromptsUseAutoBehavior ?? false; + const shouldUseDataFromConfig = config?.skipPromptsUseAutoBehavior ?? false; // Initialize project configuration const projectConfig = await initializeProjectConfig( @@ -89,14 +88,13 @@ export async function createWebProject({ // Generate initial configs with default deployment service await generateProjectConfigs( - memory, projectPath, projectName, frontendUsername, "vercel", initialDomain, defaultI18nShouldBeEnabled, - shouldInstallDeps, + frontendUsername, ); // Handle deployment diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getMainMenuOptions.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getMainMenuOptions.ts index cbd4fef8..9416b4a0 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getMainMenuOptions.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getMainMenuOptions.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import path from "pathe"; import pc from "picocolors"; -import { detectProjectsWithReliverse } from "~/app/menu/create-project/cp-modules/cli-main-modules/detections/detectReliverseProjects.js"; +import { detectProjectsWithReliverse } from "~/utils/reliverseConfig.js"; export type MainMenuChoice = | "create" diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getProjectMenuOptions.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getProjectMenuOptions.ts index 759c2139..aacf6a78 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getProjectMenuOptions.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/getProjectMenuOptions.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import path from "pathe"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function getProjectMenuOptions( cwd: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/installAnyGitRepoProject.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/installAnyGitRepoProject.ts index 6ff084d5..79235a63 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/installAnyGitRepoProject.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/cli-menu-items/installAnyGitRepoProject.ts @@ -1,15 +1,12 @@ import { selectPrompt, inputPrompt } from "@reliverse/prompts"; -import type { - ReliverseConfig, - ReliverseMemory, - TemplateOption, -} from "~/types.js"; +import type { ReliverseMemory, TemplateOption } from "~/types.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; import { createWebProject } from "~/app/menu/create-project/cp-mod.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { validate } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.js"; import { buildBrandNewThing } from "~/app/menu/menu-mod.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function installAnyGitRepo( cwd: string, @@ -134,10 +131,8 @@ export async function installAnyGitRepo( i18nShouldBeEnabled: true, isDev, config: { - experimental: { - i18nBehavior: "prompt", - projectFramework: "nextjs", - }, + i18nBehavior: "prompt", + projectFramework: "nextjs", }, memory, cwd, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/appts.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/appts.ts index 16f183e6..666c734e 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/appts.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/appts.ts @@ -12,8 +12,8 @@ import pc from "picocolors"; import type { ApptsConfig } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import metadata from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/metadata.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function configureAppts({ apptsConfig }: ApptsConfig) { const apptsConfigPath = join(apptsConfig, "app.ts"); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/biome.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/biome.ts index 5750afc6..bb00f41e 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/biome.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/biome.ts @@ -4,9 +4,8 @@ import fs from "fs-extra"; import path from "pathe"; import { type BiomeConfig, type ConfigPaths } from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; -import { addConfigMetadata } from "./miscellaneousConfigHelpers.js"; +import { addConfigMetadata } from "~/utils/configHandler.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const BIOME_DEFAULT_CONFIG: BiomeConfig = addConfigMetadata({ $schema: "https://biomejs.dev/schemas/1.5.3/schema.json", diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/env.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/env.ts index 1122a2f2..160e91e8 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/env.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/env.ts @@ -5,7 +5,7 @@ import { join } from "pathe"; import type { PromptType, Question } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // TODO: 🐞 Still in development! Please use at own risk! diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/envjs.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/envjs.ts index 7745da82..f744d748 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/envjs.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/envjs.ts @@ -3,8 +3,8 @@ import { selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { type ConfigPaths } from "~/types.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const ENV_DEFAULT_CONFIG = `import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/eslint.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/eslint.ts index cf7ae9ff..548675d0 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/eslint.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/eslint.ts @@ -3,8 +3,8 @@ import { selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { type ConfigPaths } from "~/types.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const ESLINT_DEFAULT_CONFIG = `// @ts-check diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/generateDefaultRulesForProject.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/generateDefaultRulesForProject.ts deleted file mode 100644 index 8015e3c1..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/generateDefaultRulesForProject.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { safeDestr } from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { ReliverseConfig } from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; -import { detectProjectType } from "./miscellaneousConfigHelpers.js"; -import { getDefaultReliverseConfig } from "./reliverseReadWrite.js"; - -export async function generateDefaultRulesForProject( - cwd: string, -): Promise { - const projectType = await detectProjectType(cwd); - if (!projectType) { - return null; - } - - const packageJsonPath = path.join(cwd, "package.json"); - let packageJson: any = {}; - try { - if (await fs.pathExists(packageJsonPath)) { - packageJson = safeDestr(await fs.readFile(packageJsonPath, "utf-8")); - } - } catch (error) { - relinka( - "error", - "Error reading package.json:", - error instanceof Error ? error.message : String(error), - ); - } - - const rules = await getDefaultReliverseConfig( - (packageJson.name as string) ?? path.basename(cwd), - (packageJson.author as string) ?? "user", - projectType, - ); - - // Detect additional features - const hasI18n = await fs.pathExists(path.join(cwd, "src/app/[locale]")); - const hasPrisma = await fs.pathExists(path.join(cwd, "prisma/schema.prisma")); - const hasDrizzle = await fs.pathExists(path.join(cwd, "drizzle.config.ts")); - const hasNextAuth = await fs.pathExists( - path.join(cwd, "src/app/api/auth/[...nextauth]"), - ); - const hasClerk = packageJson.dependencies?.["@clerk/nextjs"]; - - if (!rules.experimental) { - rules.experimental = {}; - } - - rules.experimental.features = { - ...rules.experimental.features, - i18n: hasI18n, - database: hasPrisma ?? hasDrizzle, - authentication: hasNextAuth ?? !!hasClerk, - analytics: false, - themeMode: "dark-light", - api: false, - testing: false, - docker: false, - ci: false, - commands: [], - webview: [], - language: [], - themes: [], - }; - - if (!rules.experimental.preferredLibraries) { - rules.experimental.preferredLibraries = {}; - } - - if (hasPrisma) { - rules.experimental.preferredLibraries.database = "prisma"; - } else if (hasDrizzle) { - rules.experimental.preferredLibraries.database = "drizzle"; - } - - if (hasNextAuth) { - rules.experimental.preferredLibraries.authentication = "next-auth"; - } else if (hasClerk) { - rules.experimental.preferredLibraries.authentication = "clerk"; - } - - return rules; -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/knip.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/knip.ts index 8465be12..cbffd50f 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/knip.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/knip.ts @@ -3,10 +3,9 @@ import { selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { type KnipConfig, type ConfigPaths } from "~/types.js"; - -import { addConfigMetadata } from "./miscellaneousConfigHelpers.js"; +import { addConfigMetadata } from "~/utils/configHandler.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const KNIP_DEFAULT_CONFIG: KnipConfig = addConfigMetadata({ $schema: "https://unpkg.com/knip@latest/schema.json", diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/miscellaneousConfigHelpers.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/miscellaneousConfigHelpers.ts deleted file mode 100644 index 060b8f90..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/miscellaneousConfigHelpers.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { parseJSONC } from "confbox"; -import destr from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { - BaseConfig, - BiomeConfig, - BiomeConfigResult, - ConfigFile, - ProjectTypeOptions, - ReliverseConfig, -} from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; -import { DEFAULT_CONFIG } from "./reliverseDefaultConfig.js"; - -export const CONFIG_FILES: ConfigFile[] = [ - { - name: "TypeScript", - files: ["tsconfig.json"], - editPrompt: "Edit TypeScript configuration", - }, - { - name: "Biome", - files: ["biome.json", "biome.jsonc"], - editPrompt: "Edit Biome configuration", - }, - { - name: "Knip", - files: ["knip.json", "knip.jsonc"], - editPrompt: "Edit Knip configuration", - }, - { - name: "ESLint", - files: ["eslint.config.js"], - editPrompt: "Edit ESLint configuration", - }, - { - name: "Vitest", - files: ["vitest.config.ts"], - editPrompt: "Edit Vitest configuration", - }, - { - name: "Prettier", - files: [ - ".prettierrc", - ".prettierrc.json", - ".prettierrc.yml", - ".prettierrc.yaml", - ".prettierrc.json5", - ".prettierrc.js", - "prettier.config.js", - ], - editPrompt: "Edit Prettier configuration", - }, -]; - -export async function detectConfigFiles(cwd: string): Promise { - const detectedConfigs: ConfigFile[] = []; - - for (const config of CONFIG_FILES) { - for (const file of config.files) { - if (await fs.pathExists(path.join(cwd, file))) { - detectedConfigs.push(config); - break; - } - } - } - - return detectedConfigs; -} - -// Helper function to add metadata to configs -export function addConfigMetadata(config: T): T & BaseConfig { - return { - ...config, - version: "0.1.0", - generatedAt: new Date().toISOString(), - }; -} - -export async function getReliverseConfig( - cwd: string, -): Promise { - const reliversePath = path.join(cwd, ".reliverse"); - let config: ReliverseConfig = { ...DEFAULT_CONFIG }; - - try { - // Check if .reliverse exists and read it - if (await fs.pathExists(reliversePath)) { - const configContent = await fs.readFile(reliversePath, "utf-8"); - const userConfig = destr>(configContent); - config = { ...config, ...userConfig }; - } - - // Add experimental configuration - config = { - ...config, - experimental: { - ...config.experimental, - // Project details - projectName: config.experimental?.projectName ?? "", - projectAuthor: config.experimental?.projectAuthor ?? "", - projectDescription: config.experimental?.projectDescription ?? "", - projectVersion: config.experimental?.projectVersion ?? "", - projectLicense: config.experimental?.projectLicense ?? "", - projectRepository: config.experimental?.projectRepository ?? "", - productionBranch: config.experimental?.productionBranch ?? "main", - deployUrl: config.experimental?.deployUrl ?? "", - projectActivation: config.experimental?.projectActivation ?? "auto", - projectCategory: config.experimental?.projectCategory ?? "website", - projectType: config.experimental?.projectType ?? "library", - projectDeployService: - config.experimental?.projectDeployService ?? "vercel", - projectDisplayName: config.experimental?.projectDisplayName ?? "", - projectDomain: config.experimental?.projectDomain ?? "", - projectState: config.experimental?.projectState ?? "creating", - projectSubcategory: - config.experimental?.projectSubcategory ?? "e-commerce", - projectTemplate: - config.experimental?.projectTemplate ?? "blefnk/relivator", - - // Project features - features: { - i18n: false, - analytics: false, - themeMode: "dark-light", - authentication: false, - api: false, - database: false, - testing: false, - docker: false, - ci: false, - commands: [], - webview: [], - language: [], - themes: [], - ...config.experimental?.features, - }, - - // Development preferences - projectFramework: config.experimental?.projectFramework ?? "nextjs", - projectFrameworkVersion: - config.experimental?.projectFrameworkVersion ?? "", - nodeVersion: config.experimental?.nodeVersion ?? "latest", - runtime: config.experimental?.runtime ?? "nodejs", - projectPackageManager: - config.experimental?.projectPackageManager ?? "npm", - monorepo: config.experimental?.monorepo ?? { - type: "turborepo", - packages: [], - sharedPackages: [], - }, - preferredLibraries: { - stateManagement: "zustand", - styling: "tailwind", - database: "drizzle", - testing: "vitest", - linting: "eslint", - formatting: "biome", - deployment: "vercel", - authentication: "clerk", - payment: "stripe", - analytics: "vercel", - formManagement: "react-hook-form", - uiComponents: "shadcn-ui", - monitoring: "sentry", - logging: "axiom", - forms: "react-hook-form", - validation: "zod", - documentation: "starlight", - components: "shadcn", - icons: "lucide", - mail: "resend", - search: "algolia", - cache: "redis", - storage: "cloudflare", - cdn: "cloudflare", - api: "trpc", - cms: "contentlayer", - i18n: "next-intl", - seo: "next-seo", - ui: "radix", - motion: "framer", - charts: "recharts", - dates: "dayjs", - markdown: "mdx", - security: "auth", - notifications: "sonner", - uploads: "uploadthing", - routing: "next", - ...config.experimental?.preferredLibraries, - }, - - // Code style preferences - codeStyle: { - lineWidth: 80, - cjsToEsm: true, - importSymbol: "import", - indentSize: 2, - indentStyle: "space", - dontRemoveComments: false, - shouldAddComments: true, - typeOrInterface: "type", - importOrRequire: "import", - quoteMark: "double", - semicolons: true, - trailingComma: "all", - bracketSpacing: true, - arrowParens: "always", - tabWidth: 2, - jsToTs: false, - modernize: { - replaceFs: true, - replacePath: true, - replaceHttp: true, - replaceProcess: true, - replaceConsole: true, - replaceEvents: true, - ...config.experimental?.codeStyle?.modernize, - }, - ...config.experimental?.codeStyle, - }, - - // Dependencies management - ignoreDependencies: config.experimental?.ignoreDependencies ?? [], - - // Custom rules - customRules: { - ...config.experimental?.customRules, - }, - - // Generation preferences - skipPromptsUseAutoBehavior: - config.experimental?.skipPromptsUseAutoBehavior ?? false, - deployBehavior: config.experimental?.deployBehavior ?? "prompt", - depsBehavior: config.experimental?.depsBehavior ?? "prompt", - gitBehavior: config.experimental?.gitBehavior ?? "prompt", - i18nBehavior: config.experimental?.i18nBehavior ?? "prompt", - scriptsBehavior: config.experimental?.scriptsBehavior ?? "prompt", - }, - }; - } catch (error) { - relinka( - "error-verbose", - "Error reading configuration files:", - error instanceof Error ? error.message : String(error), - ); - } - - return config; -} - -let cachedBiomeConfig: BiomeConfigResult = null; - -export async function getBiomeConfig( - projectPath: string, -): Promise { - if (cachedBiomeConfig !== null) { - return cachedBiomeConfig; - } - - try { - const biomePath = path.join(projectPath, "biome.jsonc"); - if (await fs.pathExists(biomePath)) { - const content = await fs.readFile(biomePath, "utf-8"); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - const config = parseJSONC(content) as BiomeConfig; - cachedBiomeConfig = { - lineWidth: config.formatter?.lineWidth ?? 80, - indentStyle: config.formatter?.indentStyle ?? "space", - indentWidth: config.formatter?.indentWidth ?? 2, - quoteMark: config.javascript?.formatter?.quoteStyle ?? "double", - semicolons: config.javascript?.formatter?.semicolons === "always", - trailingComma: config.javascript?.formatter?.trailingComma === "all", - }; - return cachedBiomeConfig; - } - } catch (error) { - relinka( - "error-verbose", - "Error reading biome config:", - error instanceof Error ? error.message : String(error), - ); - } - cachedBiomeConfig = null; - return null; -} - -export const PROJECT_TYPE_FILES = { - "": [], - library: ["jsr.json", "jsr.jsonc"], - nextjs: ["next.config.js", "next.config.ts", "next.config.mjs"], - astro: ["astro.config.js", "astro.config.ts", "astro.config.mjs"], - react: ["vite.config.js", "vite.config.ts", "react.config.js"], - vue: ["vue.config.js", "vite.config.ts"], - svelte: ["svelte.config.js", "svelte.config.ts"], -} satisfies Record; - -export async function detectProjectType( - cwd: string, -): Promise { - for (const [type, files] of Object.entries(PROJECT_TYPE_FILES)) { - for (const file of files) { - if (await fs.pathExists(path.join(cwd, file))) { - return type as keyof typeof PROJECT_TYPE_FILES; - } - } - } - return null; -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/nextjs.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/nextjs.ts index 923dcf0a..c1ff131d 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/nextjs.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/nextjs.ts @@ -3,10 +3,9 @@ import { selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { type NextJsConfig, type ConfigPaths } from "~/types.js"; - -import { addConfigMetadata } from "./miscellaneousConfigHelpers.js"; +import { addConfigMetadata } from "~/utils/configHandler.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const NEXTJS_DEFAULT_CONFIG: NextJsConfig = addConfigMetadata({ reactStrictMode: true, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/parseCodeStyleFromConfigs.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/parseCodeStyleFromConfigs.ts deleted file mode 100644 index 2c121b22..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/parseCodeStyleFromConfigs.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { TSConfig } from "pkg-types"; - -import { safeDestr } from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { ReliverseConfig } from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; - -export async function parseCodeStyleFromConfigs( - cwd: string, -): Promise> { - const codeStyle: any = {}; - - // Try to read TypeScript config - try { - const tsConfigPath = path.join(cwd, "tsconfig.json"); - if (await fs.pathExists(tsConfigPath)) { - const tsConfig = safeDestr( - await fs.readFile(tsConfigPath, "utf-8"), - ); - - if (tsConfig?.compilerOptions) { - const { compilerOptions } = tsConfig; - - // Detect strict mode settings - codeStyle.strictMode = { - enabled: compilerOptions.strict ?? false, - noImplicitAny: compilerOptions.noImplicitAny ?? false, - strictNullChecks: compilerOptions.strictNullChecks ?? false, - }; - - // Detect module settings - if ( - (compilerOptions.module as string)?.toLowerCase().includes("node") - ) { - codeStyle.importOrRequire = "esm"; - } - } - } - } catch (error) { - relinka( - "warn-verbose", - "Error parsing TypeScript config:", - error instanceof Error ? error.message : String(error), - ); - } - - return { experimental: { codeStyle } }; -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/putout.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/putout.ts index a7c61fde..37082805 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/putout.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/putout.ts @@ -3,10 +3,9 @@ import { selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { type PutoutConfig, type ConfigPaths } from "~/types.js"; - -import { addConfigMetadata } from "./miscellaneousConfigHelpers.js"; +import { addConfigMetadata } from "~/utils/configHandler.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const PUTOUT_DEFAULT_CONFIG: PutoutConfig = addConfigMetadata({ rules: { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseDefaultConfig.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseDefaultConfig.ts deleted file mode 100644 index a0e47d3a..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseDefaultConfig.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { ReliverseConfig } from "~/types.js"; - -export const DEFAULT_CONFIG: ReliverseConfig = { - experimental: { - // Project details - projectAuthor: "", - projectState: "", - projectDomain: "", - projectType: "", - projectCategory: "", - projectSubcategory: "", - - // Development preferences - projectFramework: "nextjs", - projectPackageManager: "bun", - preferredLibraries: { - stateManagement: "zustand", - formManagement: "react-hook-form", - styling: "tailwind", - uiComponents: "shadcn-ui", - testing: "bun", - authentication: "clerk", - database: "drizzle", - api: "trpc", - }, - - // Project features - features: { - i18n: true, - analytics: false, - themeMode: "dark-light", - authentication: true, - api: true, - database: true, - testing: false, - docker: false, - ci: false, - commands: [], - webview: [], - language: [], - themes: [], - }, - - // Code style preferences - codeStyle: { - dontRemoveComments: true, - shouldAddComments: true, - typeOrInterface: "type", - importOrRequire: "import", - quoteMark: "double", - semicolons: true, - lineWidth: 80, - indentStyle: "space", - indentSize: 2, - importSymbol: "~", - trailingComma: "all", - bracketSpacing: true, - arrowParens: "always", - tabWidth: 2, - }, - - // Generation preferences - skipPromptsUseAutoBehavior: false, - deployBehavior: "prompt", - depsBehavior: "prompt", - gitBehavior: "prompt", - i18nBehavior: "prompt", - scriptsBehavior: "prompt", - }, -}; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseFileOps.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseFileOps.ts deleted file mode 100644 index dac93612..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseFileOps.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { destr } from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { ReliverseConfig } from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; -import { getDefaultReliverseConfig } from "./reliverseReadWrite.js"; -import { reliverseConfigSchema } from "./reliverseSchema.js"; - -const BACKUP_EXTENSION = ".backup"; -const TEMP_EXTENSION = ".tmp"; - -// Types for comment sections -type CommentSection = { - title: string; - fields: Partial< - Record, string[]> - >; -}; - -type CommentSections = { - experimental: CommentSection; -}; - -/** - * Safely writes config to file with backup and atomic operations - */ -export async function safeWriteConfig( - projectPath: string, - config: ReliverseConfig, -): Promise { - const configPath = path.join(projectPath, ".reliverse"); - const backupPath = configPath + BACKUP_EXTENSION; - const tempPath = configPath + TEMP_EXTENSION; - - try { - // Validate config before writing - const validationResult = reliverseConfigSchema.safeParse(config); - if (!validationResult.success) { - throw new Error( - `Invalid config: ${validationResult.error.errors - .map((e) => `${e.path.join(".")}: ${e.message}`) - .join(", ")}`, - ); - } - - // Helper function to create comment - const c = (text: string) => `// ${text}`; - - // Define comment sections with only essential comments - const commentSections: CommentSections = { - experimental: { - title: c("Unstable features"), - fields: { - skipPromptsUseAutoBehavior: [ - c("Do you want autoYes/autoNo below?"), - c("Set to true to activate auto-answering."), - c("This is to ensure there is no unexpected behavior."), - ], - features: [c("Project capabilities")], - projectFramework: [c("Tech stack of your project")], - codeStyle: [c("Code style preferences")], - ignoreDependencies: [c("Cleaner codemod will ignore these deps")], - customRules: [c("Custom rules for Reliverse AI")], - deployBehavior: [c("Prompts behavior (prompt | autoYes | autoNo)")], - }, - }, - }; - - // Format with 2 spaces indentation - let content = JSON.stringify(validationResult.data, null, 2); - - // Add section comments - Object.entries(commentSections).forEach(([section, { title, fields }]) => { - // Add section title with proper spacing - content = content.replace(`"${section}":`, `${title}\n "${section}":`); - - // Add field comments - Object.entries(fields).forEach(([field, comments]) => { - const fieldPattern = new RegExp(`(\\s+)"${field}":`, "g"); - // Add proper indentation for each comment line - const formattedComments = comments - .map( - (comment, index, array) => - index === array.length - 1 - ? ` ${comment}` // Last comment - : ` ${comment}\n`, // Other comments - ) - .join(""); - content = content.replace( - fieldPattern, - `\n\n${formattedComments}\n "${field}":`, - ); - }); - }); - - // Clean up multiple empty lines and ensure final newline - content = `${content - .replace(/\n{3,}/g, "\n\n") // Replace 3 or more newlines with 2 - .replace(/{\n\n/g, "{\n") // Remove double newline after opening brace - .replace(/\n\n}/g, "\n}") // Remove double newline before closing brace - .trim()}\n`; // Ensure single newline at end - - // Create backup if file exists - if (await fs.pathExists(configPath)) { - await fs.copy(configPath, backupPath); - } - - // Write to temp file first - await fs.writeFile(tempPath, content); - - // Atomically rename temp file to actual file - await fs.rename(tempPath, configPath); - - // Remove backup on success - if (await fs.pathExists(backupPath)) { - await fs.remove(backupPath); - } - - relinka("success-verbose", "Config written successfully"); - } catch (error) { - // Restore from backup if write failed - if ( - (await fs.pathExists(backupPath)) && - !(await fs.pathExists(configPath)) - ) { - await fs.copy(backupPath, configPath); - relinka("warn", "Restored config from backup after failed write"); - } - - // Clean up temp file - if (await fs.pathExists(tempPath)) { - await fs.remove(tempPath); - } - - throw error; - } -} - -/** - * Safely reads and validates config from file - */ -export async function safeGetReliverseConfig( - projectPath: string, -): Promise { - const configPath = path.join(projectPath, ".reliverse"); - const backupPath = configPath + BACKUP_EXTENSION; - - try { - // Try reading main file - if (await fs.pathExists(configPath)) { - const content = await fs.readFile(configPath, "utf-8"); - - // Handle empty file or just {} - if (!content.trim() || content.trim() === "{}") { - const defaultConfig = await getDefaultReliverseConfig( - path.basename(projectPath), - "user", - ); - await safeWriteConfig(projectPath, defaultConfig); - return defaultConfig; - } - - const parsed = destr(content); - if (!parsed || typeof parsed !== "object") { - const defaultConfig = await getDefaultReliverseConfig( - path.basename(projectPath), - "user", - ); - await safeWriteConfig(projectPath, defaultConfig); - return defaultConfig; - } - - // Validate parsed content - const validationResult = reliverseConfigSchema.safeParse(parsed); - if (validationResult.success) { - // If config is valid, return it as is without any merging - return validationResult.data; - } - - // Only merge if there are missing required fields - const missingFields = validationResult.error.errors.some( - (e) => e.code === "invalid_type" && e.received === "undefined", - ); - - if (!missingFields) { - // If errors are not about missing fields, return null to trigger backup/default - relinka( - "warn", - `Invalid config format: ${validationResult.error.errors - .map((e) => `${e.path.join(".")}: ${e.message}`) - .join(", ")}`, - ); - return null; - } - - // If we have missing fields, merge with defaults - const defaultConfig = await getDefaultReliverseConfig( - path.basename(projectPath), - "user", - ); - - // Deep merge existing values with defaults - const mergedConfig: ReliverseConfig = { - experimental: { - ...defaultConfig.experimental, - ...(parsed as Partial)?.experimental, - features: { - ...defaultConfig.experimental?.features, - ...(parsed as Partial)?.experimental?.features, - }, - codeStyle: { - ...defaultConfig.experimental?.codeStyle, - ...(parsed as Partial)?.experimental?.codeStyle, - }, - preferredLibraries: { - ...defaultConfig.experimental?.preferredLibraries, - ...(parsed as Partial)?.experimental - ?.preferredLibraries, - }, - customRules: { - ...defaultConfig.experimental?.customRules, - ...(parsed as Partial)?.experimental?.customRules, - }, - }, - }; - - // Validate merged config - const mergedValidation = reliverseConfigSchema.safeParse(mergedConfig); - if (mergedValidation.success) { - await safeWriteConfig(projectPath, mergedValidation.data); - relinka( - "info", - "Updated config with missing fields while preserving existing values", - ); - return mergedValidation.data; - } - - // If merged config is invalid, warn and try backup - relinka( - "warn", - `Invalid config format: ${validationResult.error.errors - .map((e) => `${e.path.join(".")}: ${e.message}`) - .join(", ")}`, - ); - } - - // Try reading backup if main file is invalid or missing - if (await fs.pathExists(backupPath)) { - const backupContent = await fs.readFile(backupPath, "utf-8"); - const parsed = destr(backupContent); - - const validationResult = reliverseConfigSchema.safeParse(parsed); - if (validationResult.success) { - // Restore from backup - await fs.copy(backupPath, configPath); - relinka("info", "Restored config from backup"); - return validationResult.data; - } - } - - // If no valid config found, create default - const defaultConfig = await getDefaultReliverseConfig( - path.basename(projectPath), - "user", - ); - await safeWriteConfig(projectPath, defaultConfig); - return defaultConfig; - } catch (error) { - relinka( - "error", - "Error reading config:", - error instanceof Error ? error.message : String(error), - ); - return null; - } -} - -/** - * Safely updates specific fields in the config - */ -export async function safeUpdateConfig( - projectPath: string, - updates: Partial, -): Promise { - try { - const currentConfig = await safeGetReliverseConfig(projectPath); - if (!currentConfig) { - return false; - } - - const updatedConfig = { - ...currentConfig, - experimental: { - ...currentConfig.experimental, - ...updates.experimental, - }, - }; - - await safeWriteConfig(projectPath, updatedConfig); - return true; - } catch (error) { - relinka( - "error", - "Error updating config:", - error instanceof Error ? error.message : String(error), - ); - return false; - } -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseReadWrite.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseReadWrite.ts deleted file mode 100644 index c1287cbf..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseReadWrite.ts +++ /dev/null @@ -1,112 +0,0 @@ -import type { PackageJson } from "pkg-types"; - -import { readPackageJSON } from "pkg-types"; - -import type { ReliverseConfig } from "~/types.js"; - -import { getBiomeConfig } from "./miscellaneousConfigHelpers.js"; -import { safeGetReliverseConfig, safeWriteConfig } from "./reliverseFileOps.js"; - -export async function writeReliverseConfig( - projectPath: string, - rules: ReliverseConfig, -): Promise { - await safeWriteConfig(projectPath, rules); -} - -export async function readReliverseConfig( - projectPath: string, -): Promise { - return safeGetReliverseConfig(projectPath); -} - -export async function getDefaultReliverseConfig( - projectName: string, - projectAuthor: string, - projectFramework = "nextjs", -): Promise { - const biomeConfig = await getBiomeConfig(process.cwd()); - - // Read package.json - let packageData: PackageJson = { name: projectName, author: projectAuthor }; - - try { - packageData = await readPackageJSON(); - } catch { - // Use default values if package.json doesn't exist - } - - return { - experimental: { - // Project details - projectName: packageData.name ?? projectName, - projectAuthor: - typeof packageData.author === "object" - ? (packageData.author.name ?? projectAuthor) - : (packageData.author ?? projectAuthor), - projectDescription: packageData.description ?? "", - projectVersion: packageData.version ?? "0.1.0", - projectLicense: packageData.license ?? "MIT", - projectRepository: - (typeof packageData.repository === "string" - ? packageData.repository - : packageData.repository?.url) ?? "", - - // Project features - features: { - i18n: false, - analytics: false, - themeMode: "dark-light", - authentication: false, - api: false, - database: false, - testing: false, - docker: false, - ci: false, - commands: [], - webview: [], - language: [], - themes: [], - }, - - // Development preferences - projectFramework, - projectPackageManager: "npm", - projectFrameworkVersion: undefined, - nodeVersion: undefined, - runtime: undefined, - monorepo: { - type: "none", - packages: [], - sharedPackages: [], - }, - preferredLibraries: {}, - codeStyle: { - lineWidth: biomeConfig?.lineWidth ?? 80, - indentSize: biomeConfig?.indentWidth ?? 2, - indentStyle: "space", - quoteMark: "double", - semicolons: true, - trailingComma: "all", - bracketSpacing: true, - arrowParens: "always", - tabWidth: 2, - jsToTs: false, - }, - - // Dependencies management - ignoreDependencies: [], - - // Custom rules - customRules: {}, - - // Generation preferences - skipPromptsUseAutoBehavior: false, - deployBehavior: "prompt", - depsBehavior: "prompt", - gitBehavior: "prompt", - i18nBehavior: "prompt", - scriptsBehavior: "prompt", - }, - }; -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseSchema.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseSchema.ts deleted file mode 100644 index 501a540e..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseSchema.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { z } from "zod"; - -import type { ReliverseConfig } from "~/types.js"; - -// Feature schema -const featuresSchema = z.object({ - i18n: z.boolean().default(true), - analytics: z.boolean().default(false), - themeMode: z.enum(["light", "dark", "dark-light"]).default("dark-light"), - authentication: z.boolean().default(true), - api: z.boolean().default(true), - database: z.boolean().default(true), - testing: z.boolean().default(false), - docker: z.boolean().default(false), - ci: z.boolean().default(false), - commands: z.array(z.string()).default([]), - webview: z.array(z.string()).default([]), - language: z.array(z.string()).default(["typescript"]), - themes: z.array(z.string()).default(["default"]), -}); - -// Code style schema -const codeStyleSchema = z.object({ - lineWidth: z.number().min(1).max(200).default(80), - indentSize: z.number().min(1).max(8).default(2), - indentStyle: z.enum(["space", "tab"]).default("space"), - quoteMark: z.enum(["single", "double"]).default("double"), - semicolons: z.boolean().default(true), - trailingComma: z.enum(["none", "es5", "all"]).default("all"), - bracketSpacing: z.boolean().default(true), - arrowParens: z.enum(["always", "avoid"]).default("always"), - tabWidth: z.number().min(1).max(8).default(2), - jsToTs: z.boolean().default(false), -}); - -const monorepoSchema = z - .object({ - type: z.enum(["none", "turborepo", "nx", "pnpm"]), - packages: z.array(z.string()), - sharedPackages: z.array(z.string()), - }) - .default({ - type: "none", - packages: [], - sharedPackages: [], - }); - -// Experimental schema -const experimentalSchema = z.object({ - // Project details - projectName: z.string().min(1), - projectAuthor: z.string().min(1), - projectDescription: z.string().default(""), - projectVersion: z - .string() - .regex(/^\d+\.\d+\.\d+/) - .default("0.1.0"), - projectLicense: z.string().default("MIT"), - projectRepository: z.string().url().optional(), - projectDeployService: z.enum(["vercel", "netlify", "railway"]).optional(), - projectDomain: z.string().url().optional(), - - // Project features - features: featuresSchema.default({}), - - // Development preferences - projectFramework: z.string().default("nextjs"), - projectPackageManager: z.enum(["npm", "pnpm", "yarn", "bun"]).default("npm"), - projectFrameworkVersion: z.string().optional(), - nodeVersion: z.string().optional(), - runtime: z.string().optional(), - monorepo: monorepoSchema, - preferredLibraries: z.record(z.string()).default({}), - codeStyle: codeStyleSchema.default({}), - - // Dependencies management - ignoreDependencies: z.array(z.string()).default([]), - - // Custom rules - customRules: z.record(z.unknown()).default({}), - - // Generation preferences - skipPromptsUseAutoBehavior: z.boolean().default(false), - deployBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), - depsBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), - gitBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), - i18nBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), - scriptsBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), -}); - -export const reliverseConfigSchema: z.ZodType = z.object({ - experimental: experimentalSchema, -}); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/validateAndInsertMissingKeys.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/configs/validateAndInsertMissingKeys.ts deleted file mode 100644 index 9c5442c2..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/configs/validateAndInsertMissingKeys.ts +++ /dev/null @@ -1,40 +0,0 @@ -import fs from "fs-extra"; -import path from "pathe"; - -import { relinka } from "../handlers/logger.js"; -import { getDefaultReliverseConfig } from "./reliverseReadWrite.js"; - -export async function validateAndInsertMissingKeys(cwd: string): Promise { - try { - const configPath = path.join(cwd, ".reliverse"); - - // Check if .reliverse exists - if (!(await fs.pathExists(configPath))) { - return; - } - - // Read current config - const content = await fs.readFile(configPath, "utf-8"); - - // Only proceed if file is empty or contains only {} - if (!content.trim() || content.trim() === "{}") { - const defaultRules = await getDefaultReliverseConfig( - path.basename(cwd), - "user", - "nextjs", // fallback default - ); - - await fs.writeFile(configPath, JSON.stringify(defaultRules, null, 2)); - relinka( - "info", - "Created initial .reliverse configuration. Please review and adjust as needed.", - ); - } - } catch (error) { - relinka( - "error-verbose", - "Error validating .reliverse:", - error instanceof Error ? error.message : String(error), - ); - } -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectReliverseProjects.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectReliverseProjects.ts deleted file mode 100644 index 0a9a59fe..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectReliverseProjects.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { parseJSONC } from "confbox"; -import { destr } from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { DetectedProject, ReliverseConfig } from "~/types.js"; - -import { relinka } from "../handlers/logger.js"; - -async function checkProjectFiles(projectPath: string): Promise<{ - hasReliverse: boolean; - hasPackageJson: boolean; - hasNodeModules: boolean; - hasGit: boolean; -}> { - const [hasReliverse, hasPackageJson, hasNodeModules, hasGit] = - await Promise.all([ - fs.pathExists(path.join(projectPath, ".reliverse")), - fs.pathExists(path.join(projectPath, "package.json")), - fs.pathExists(path.join(projectPath, "node_modules")), - fs.pathExists(path.join(projectPath, ".git")), - ]); - - return { hasReliverse, hasPackageJson, hasNodeModules, hasGit }; -} - -export async function detectProject( - projectPath: string, -): Promise { - try { - const { hasReliverse, hasPackageJson, hasNodeModules, hasGit } = - await checkProjectFiles(projectPath); - - if (!hasReliverse || !hasPackageJson) { - return null; - } - - const configContent = await fs.readFile( - path.join(projectPath, ".reliverse"), - "utf-8", - ); - const parsedConfig = parseJSONC(configContent); - const config = destr(parsedConfig); - - return { - name: path.basename(projectPath), - path: projectPath, - config, - needsDepsInstall: !hasNodeModules, - hasGit, - }; - } catch (error) { - relinka( - "warn", - `Error processing ${projectPath}: ${error instanceof Error ? error.message : String(error)}`, - ); - return null; - } -} - -export async function detectProjectsWithReliverse( - cwd: string, -): Promise { - const detectedProjects: DetectedProject[] = []; - - // First check the root directory - const rootProject = await detectProject(cwd); - if (rootProject) { - detectedProjects.push(rootProject); - } - - // Then check subdirectories - try { - const items = await fs.readdir(cwd, { withFileTypes: true }); - for (const item of items) { - if (item.isDirectory()) { - const projectPath = path.join(cwd, item.name); - const project = await detectProject(projectPath); - if (project) { - detectedProjects.push(project); - } - } - } - } catch (error) { - relinka( - "warn", - `Error reading directory ${cwd}: ${error instanceof Error ? error.message : String(error)}`, - ); - } - - return detectedProjects; -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectedProjectsMenu.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectedProjectsMenu.ts index c50b7615..6328e614 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectedProjectsMenu.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/detections/detectedProjectsMenu.ts @@ -7,10 +7,20 @@ import { import { installDependencies } from "nypm"; import pc from "picocolors"; -import type { DetectedProject, ReliverseMemory } from "~/types.js"; +import type { ReliverseMemory } from "~/types.js"; +import type { DetectedProject } from "~/utils/reliverseConfig.js"; import { experimental } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { checkScriptExists } from "~/app/menu/create-project/cp-impl.js"; +import { manageDrizzleSchema } from "~/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchema.js"; +import { + convertDatabaseProvider, + convertPrismaToDrizzle, +} from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDatabase.js"; +import { handleCleanup } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCleanup.js"; +import { handleCodemods } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCodemods.js"; +import { handleConfigEditing } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleConfigEdits.js"; +import { handleIntegrations } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleIntegrations.js"; import { readShadcnConfig, getInstalledComponents, @@ -31,20 +41,10 @@ import { initGit, createGithubRepository, } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/git.js"; - -import { checkScriptExists } from "../../../cp-impl.js"; -import { checkGithubRepoOwnership } from "../../git-deploy-prompts/github.js"; -import { ensureDbInitialized } from "../../git-deploy-prompts/helpers/handlePkgJsonScripts.js"; -import { createOctokitInstance } from "../../git-deploy-prompts/octokit-instance.js"; -import { manageDrizzleSchema } from "../drizzle/manageDrizzleSchema.js"; -import { - convertDatabaseProvider, - convertPrismaToDrizzle, -} from "../handlers/codemods/convertDatabase.js"; -import { handleCleanup } from "../handlers/handleCleanup.js"; -import { handleCodemods } from "../handlers/handleCodemods.js"; -import { handleConfigEditing } from "../handlers/handleConfigEdits.js"; -import { handleIntegrations } from "../handlers/handleIntegrations.js"; +import { checkGithubRepoOwnership } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/github.js"; +import { ensureDbInitialized } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/helpers/handlePkgJsonScripts.js"; +import { createOctokitInstance } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function showDetectedProjectsMenu( projects: DetectedProject[], @@ -486,7 +486,7 @@ export async function showDetectedProjectsMenu( } else if (action === "drizzle-schema") { await manageDrizzleSchema(selectedProject.path, false); } else if (action === "cleanup") { - await handleCleanup(selectedProject.path); + await handleCleanup(cwd, selectedProject.path, selectedProject.name); } else if (action === "edit-config") { await handleConfigEditing(selectedProject.path); } diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadGitRepo.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadGitRepo.ts index 991d1915..84334ca7 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadGitRepo.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadGitRepo.ts @@ -3,10 +3,7 @@ import fs from "fs-extra"; import path from "pathe"; import { simpleGit } from "simple-git"; -import { - relinka, - throwError, -} from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka, throwError } from "~/utils/loggerRelinka.js"; export async function downloadGitRepo( name: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadI18nFiles.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadI18nFiles.ts index 6ae1a165..46627d4c 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadI18nFiles.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadI18nFiles.ts @@ -1,7 +1,7 @@ import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function setupI18nFiles(projectPath: string): Promise { try { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchema.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchema.ts index 1df178e1..9b5504ae 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchema.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchema.ts @@ -2,7 +2,7 @@ import { selectPrompt, confirmPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { addNewTable, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchemaUtils.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchemaUtils.ts index 563b40d2..742b9ae9 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchemaUtils.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/drizzle/manageDrizzleSchemaUtils.ts @@ -4,10 +4,10 @@ import path from "pathe"; import type { ColumnType, DatabaseProvider, TableSchema } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { installIntegration } from "~/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.js"; +import { INTEGRATION_CONFIGS } from "~/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.js"; +import { relinka } from "~/utils/loggerRelinka.js"; -import { installIntegration } from "../integrations/integrations.js"; -import { INTEGRATION_CONFIGS } from "../integrations/integrationsIntegrConfig.js"; import { COLUMN_TYPES } from "./manageDrizzleConstants.js"; export async function detectDatabaseProvider( diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/cloneAndCopyFiles.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/cloneAndCopyFiles.ts index 58bfd105..03004782 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/cloneAndCopyFiles.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/cloneAndCopyFiles.ts @@ -5,9 +5,8 @@ import { simpleGit } from "simple-git"; import type { CloneError } from "~/types.js"; import type { CopyError } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - -import { FILE_CONFLICTS } from "../../../../../constants.js"; +import { FILE_CONFLICTS } from "~/app/constants.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Function to clone and copy files from the repository export async function cloneAndCopyFiles( diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertCjsToEsm.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertCjsToEsm.ts index 742bbfab..50e26b85 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertCjsToEsm.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertCjsToEsm.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function convertCjsToEsm(projectPath: string) { relinka("info", `Converting CommonJS to ESM in ${projectPath}`); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDatabase.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDatabase.ts index 8f6c9db8..2edf0e03 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDatabase.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDatabase.ts @@ -3,7 +3,7 @@ import path from "pathe"; import type { PrismaField, PrismaModel } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; function parsePrismaSchema(content: string): PrismaModel[] { const models: PrismaModel[] = []; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDefinitions.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDefinitions.ts index 296ea17d..21b1b706 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDefinitions.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertDefinitions.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function convertTypeDefinitions( projectPath: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertImportStyle.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertImportStyle.ts index 0db1c15c..90a2bf1b 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertImportStyle.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertImportStyle.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function convertImportStyle( projectPath: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertJsToTs.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertJsToTs.ts index 000d09a2..928b5a77 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertJsToTs.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertJsToTs.ts @@ -1,7 +1,7 @@ import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; function generateTypeDefinitions(content: string): string { let result = content; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertQuoteStyle.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertQuoteStyle.ts index 5cb66009..6580f584 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertQuoteStyle.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertQuoteStyle.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function convertQuoteStyle( projectPath: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertRuntime.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertRuntime.ts index 84fd9183..f0ec58bf 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertRuntime.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertRuntime.ts @@ -5,7 +5,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const RUNTIME_REPLACEMENTS = { bun: { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertTailwind.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertTailwind.ts index 9e59f3b2..0b6ea69f 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertTailwind.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertTailwind.ts @@ -4,7 +4,7 @@ import path from "pathe"; import type { TailwindThemeVariable } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { DEFAULT_THEME_VARIABLES, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertToMonorepo.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertToMonorepo.ts index e1ba1176..cb15b18c 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertToMonorepo.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/convertToMonorepo.ts @@ -3,7 +3,7 @@ import path from "pathe"; import type { MonorepoType } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const MONOREPO_CONFIGS = { turborepo: { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/removeComments.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/removeComments.ts index b13aa6f9..a7c8b3a8 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/removeComments.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/removeComments.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "../logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Cleanup's functions export async function removeComments(cwd: string): Promise { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.ts index f3232a15..b5def0b0 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; async function detectCurrentImportSymbol( projectPath: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceWithModern.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceWithModern.ts index 45de571a..eb08dc14 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceWithModern.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceWithModern.ts @@ -4,7 +4,7 @@ import path from "pathe"; import type { ModernReplacement } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const MODERN_REPLACEMENTS: ModernReplacement[] = [ // File System diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/configure.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/configure.ts index 326fc028..0577f324 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/configure.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/configure.ts @@ -6,14 +6,13 @@ import pc from "picocolors"; import { siteConfig } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/app.js"; import { configureAppts } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/appts.js"; +import { configureBiome } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/biome.js"; import { configureEnv } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/envjs.js"; import { configureEslint } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/eslint.js"; import { configureKnip } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/knip.js"; +import { configureNext } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/nextjs.js"; import { configurePutout } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/putout.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - -import { configureBiome } from "../configs/biome.js"; -import { configureNext } from "../configs/nextjs.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function runReliverseSetup() { const currentDirname = getCurrentDirname(import.meta.url); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/generateProjectConfigs.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/generateProjectConfigs.ts index 06d62723..c5e04d55 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/generateProjectConfigs.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/generateProjectConfigs.ts @@ -4,14 +4,9 @@ import fs from "fs-extra"; import path from "pathe"; import { CONFIG_CATEGORIES } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; -import { - type DeploymentService, - type ReliverseMemory, - type VSCodeSettings, -} from "~/types.js"; - -import { generateReliverseFile } from "./reliverseConfig.js"; +import { type DeploymentService, type VSCodeSettings } from "~/types.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { generateReliverseConfig } from "~/utils/reliverseConfig.js"; async function generateBiomeConfig( projectPath: string, @@ -117,7 +112,6 @@ async function generateVSCodeSettings( } async function generateConfigFiles( - memory: ReliverseMemory, projectPath: string, overwrite: boolean, projectName: string, @@ -125,7 +119,7 @@ async function generateConfigFiles( deployService: DeploymentService, primaryDomain: string, i18nShouldBeEnabled: boolean, - shouldInstallDeps: boolean, + githubUsername: string, filesToGenerate: string[] = [], ): Promise { await spinnerTaskPrompt({ @@ -146,16 +140,15 @@ async function generateConfigFiles( const configGenerators = { ".reliverse": async () => - generateReliverseFile({ + generateReliverseConfig({ projectName, frontendUsername, deployService, primaryDomain, projectPath, i18nShouldBeEnabled, - shouldInstallDeps, overwrite, - memory, + githubUsername, }), "biome.json": () => generateBiomeConfig(projectPath, overwrite), "settings.json": () => generateVSCodeSettings(projectPath, overwrite), @@ -179,14 +172,13 @@ async function generateConfigFiles( } export async function generateProjectConfigs( - memory: ReliverseMemory, projectPath: string, projectName: string, frontendUsername: string, deployService: DeploymentService, primaryDomain: string, i18nShouldBeEnabled: boolean, - shouldInstallDeps: boolean, + githubUsername: string, ): Promise { try { // Check which files exist @@ -209,7 +201,6 @@ export async function generateProjectConfigs( ); // Generate missing files without overwriting existing ones await generateConfigFiles( - memory, projectPath, false, projectName, @@ -217,12 +208,11 @@ export async function generateProjectConfigs( deployService, primaryDomain, i18nShouldBeEnabled, - shouldInstallDeps, + githubUsername, ); } else { // No existing files, generate everything await generateConfigFiles( - memory, projectPath, true, projectName, @@ -230,7 +220,7 @@ export async function generateProjectConfigs( deployService, primaryDomain, i18nShouldBeEnabled, - shouldInstallDeps, + githubUsername, ); } } catch (error) { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCleanup.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCleanup.ts index 2954b8c4..a11a362f 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCleanup.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCleanup.ts @@ -6,22 +6,34 @@ import path from "pathe"; import type { KnipConfig } from "~/types.js"; -import { readReliverseConfig } from "../configs/reliverseReadWrite.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { readReliverseConfig } from "~/utils/reliverseConfig.js"; + import { removeComments } from "./codemods/removeComments.js"; import { getUnusedDependencies } from "./codemods/removeUnusedDeps.js"; import { uninstallDependencies } from "./dependencies.js"; -import { relinka } from "./logger.js"; -export async function handleCleanup(cwd: string) { +const defaultIgnoredDeps: string[] = [ + // TODO: We should add here any default dependencies that should always be ignored +]; + +export async function handleCleanup( + cwd: string, + configPath: string, + projectName: string, +) { + const ignoredDeps = new Set(defaultIgnoredDeps); + // Try to read Knip config for ignoreDependencies - let ignoredDeps: string[] = []; try { const knipConfigPath = path.join(cwd, "knip.json"); if (await fs.pathExists(knipConfigPath)) { const knipConfig = destr( await fs.readFile(knipConfigPath, "utf-8"), ); - ignoredDeps = knipConfig.ignoreDependencies ?? []; + if (knipConfig?.ignoreDependencies) { + knipConfig.ignoreDependencies.forEach((dep) => ignoredDeps.add(dep)); + } } } catch (error) { relinka( @@ -33,11 +45,9 @@ export async function handleCleanup(cwd: string) { // Read ignoreDependencies from .reliverse if exists try { - const rules = await readReliverseConfig(cwd); - if (rules?.experimental?.ignoreDependencies) { - ignoredDeps = [ - ...new Set([...ignoredDeps, ...rules.experimental.ignoreDependencies]), - ]; + const rules = await readReliverseConfig(projectName, configPath, cwd); + if (rules?.ignoreDependencies) { + rules.ignoreDependencies.forEach((dep) => ignoredDeps.add(dep)); } } catch (error) { relinka( @@ -74,7 +84,10 @@ export async function handleCleanup(cwd: string) { await removeComments(cwd); } } else if (action === "dependencies") { - const unusedDeps = await getUnusedDependencies(cwd, ignoredDeps); + const unusedDeps = await getUnusedDependencies( + cwd, + Array.from(ignoredDeps), + ); if (unusedDeps.length === 0) { relinka("info", "No unused dependencies found!"); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCodemods.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCodemods.ts index 40219f2a..e7d35f69 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCodemods.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleCodemods.ts @@ -1,6 +1,9 @@ import { confirmPrompt, multiselectPrompt } from "@reliverse/prompts"; -import type { MonorepoType, ReliverseConfig } from "~/types.js"; +import type { MonorepoType } from "~/types.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; + +import { relinka } from "~/utils/loggerRelinka.js"; import { convertCjsToEsm } from "./codemods/convertCjsToEsm.js"; import { convertTypeDefinitions } from "./codemods/convertDefinitions.js"; @@ -12,13 +15,9 @@ import { convertTailwindV3ToV4 } from "./codemods/convertTailwind.js"; import { convertToMonorepo } from "./codemods/convertToMonorepo.js"; import { replaceImportSymbol } from "./codemods/replaceImportSymbol.js"; import { replaceWithModern } from "./codemods/replaceWithModern.js"; -import { relinka } from "./logger.js"; export async function handleCodemods(rules: ReliverseConfig, cwd: string) { - if ( - !rules.experimental?.codeStyle || - !rules.experimental?.preferredLibraries - ) { + if (!rules.codeStyle || !rules.preferredLibraries) { relinka("error", "Missing required configuration in .reliverse"); return; } @@ -26,7 +25,7 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { const availableCodemods = []; // Push: Tailwind v3 to v4 conversion codemod - if (rules.experimental.preferredLibraries.styling === "tailwind") { + if (rules.preferredLibraries?.styling === "tailwind") { availableCodemods.push({ label: "Convert Tailwind CSS v3 to v4", value: "tailwind-v4", @@ -35,11 +34,11 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { } // Push: import symbol codemod if rule exists - if (rules.experimental.codeStyle.importSymbol?.length) { + if (rules.codeStyle?.importSymbol?.length) { availableCodemods.push({ label: "Replace Import Symbols", value: "import-symbols", - hint: `${rules.experimental.codeStyle.importSymbol.length} symbol(s) to replace`, + hint: `${rules.codeStyle.importSymbol.length} symbol(s) to replace`, }); } @@ -47,31 +46,31 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { availableCodemods.push({ label: "Convert Quote Style", value: "quote-style", - hint: `Convert all quotes to ${rules.experimental.codeStyle.quoteMark}`, + hint: `Convert all quotes to ${rules.codeStyle.quoteMark}`, }); // Push: import style codemod - const importStyle = rules.experimental.codeStyle.importOrRequire; - if (importStyle && (importStyle === "import" || importStyle === "require")) { + const importStyle = rules.codeStyle?.importOrRequire; + if (importStyle && importStyle !== "mixed") { availableCodemods.push({ label: "Convert Import Style", value: "import-style", - hint: `Convert to ${importStyle} style`, + hint: `Convert all imports to ${importStyle} style`, }); } // Push: Definitions converter codemod - const typeStyle = rules.experimental.codeStyle.typeOrInterface; - if (typeStyle && (typeStyle === "type" || typeStyle === "interface")) { + const typeStyle = rules.codeStyle?.typeOrInterface; + if (typeStyle && typeStyle !== "mixed") { availableCodemods.push({ label: "Convert Type Definitions", value: "type-definitions", - hint: `Convert to ${typeStyle} style`, + hint: `Convert all declarations to ${typeStyle}s`, }); } // Push: CJS to ESM codemod if enabled - if (rules.experimental.codeStyle.cjsToEsm) { + if (rules.codeStyle?.cjsToEsm) { availableCodemods.push({ label: "Convert CommonJS to ESM", value: "cjs-to-esm", @@ -80,7 +79,7 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { } // Push: runtime conversion codemods - if (rules.experimental.runtime === "nodejs") { + if (rules.runtime === "nodejs") { availableCodemods.push( { label: "Convert to Bun", @@ -96,18 +95,18 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { } // Push: monorepo conversion codemod if configured - if (rules.experimental.monorepo?.type) { + if (rules.monorepo?.type) { availableCodemods.push({ label: "Convert to Monorepo", value: "single-to-monorepo", - hint: `Convert to ${rules.experimental.monorepo.type} monorepo`, + hint: `Convert to ${rules.monorepo.type} monorepo`, }); } // Push: modernize codemod if any modernize options are enabled if ( - rules.experimental.codeStyle.modernize && - Object.values(rules.experimental.codeStyle.modernize).some(Boolean) + rules.codeStyle?.modernize && + Object.values(rules.codeStyle.modernize).some(Boolean) ) { availableCodemods.push({ label: "Modernize Code", @@ -117,7 +116,7 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { } // Push: JS to TS codemod if enabled - if (rules.experimental.codeStyle.jsToTs) { + if (rules.codeStyle?.jsToTs) { availableCodemods.push({ label: "Convert JavaScript to TypeScript", value: "js-to-ts", @@ -148,7 +147,7 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { await convertTailwindV3ToV4(cwd); } } else if (codemod === "import-symbols") { - const targetSymbol = rules.experimental.codeStyle.importSymbol; + const targetSymbol = rules.codeStyle?.importSymbol; if (!targetSymbol) { continue; } @@ -166,21 +165,18 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { `Replaced detected import symbol with "${targetSymbol}"`, ); } - } else if ( - codemod === "quote-style" && - rules.experimental.codeStyle.quoteMark - ) { + } else if (codemod === "quote-style" && rules.codeStyle?.quoteMark) { const shouldConvert = await confirmPrompt({ - title: `Convert all quotes to ${rules.experimental.codeStyle.quoteMark}?`, + title: `Convert all quotes to ${rules.codeStyle.quoteMark}?`, defaultValue: true, }); if (shouldConvert) { - await convertQuoteStyle(cwd, rules.experimental.codeStyle.quoteMark); + await convertQuoteStyle(cwd, rules.codeStyle.quoteMark); } } else if (codemod === "import-style") { - const style = rules.experimental.codeStyle.importOrRequire; - if (style && (style === "import" || style === "require")) { + const style = rules.codeStyle?.importOrRequire; + if (style && style !== "mixed") { const shouldConvert = await confirmPrompt({ title: `Convert to ${style} style?`, defaultValue: true, @@ -191,8 +187,8 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { } } } else if (codemod === "type-definitions") { - const style = rules.experimental.codeStyle.typeOrInterface; - if (style && (style === "type" || style === "interface")) { + const style = rules.codeStyle?.typeOrInterface; + if (style && style !== "mixed") { const shouldConvert = await confirmPrompt({ title: `Convert TS definitions to ${style} style?`, defaultValue: true, @@ -229,11 +225,8 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { if (shouldConvert) { await convertRuntime(cwd, "deno"); } - } else if ( - codemod === "single-to-monorepo" && - rules.experimental.monorepo?.type - ) { - const monorepoType = rules.experimental.monorepo.type; + } else if (codemod === "single-to-monorepo" && rules.monorepo?.type) { + const monorepoType = rules.monorepo.type; if (isValidMonorepoType(monorepoType)) { const shouldConvert = await confirmPrompt({ title: `Convert to ${monorepoType} monorepo?`, @@ -244,8 +237,8 @@ export async function handleCodemods(rules: ReliverseConfig, cwd: string) { await convertToMonorepo( cwd, monorepoType, - rules.experimental.monorepo.packages, - rules.experimental.monorepo.sharedPackages, + rules.monorepo.packages, + rules.monorepo.sharedPackages, ); } } diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleConfigEdits.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleConfigEdits.ts index 329445ef..af2d7de9 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleConfigEdits.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleConfigEdits.ts @@ -5,14 +5,15 @@ import { destr } from "destr"; import fs from "fs-extra"; import path from "pathe"; -import { ESLINT_PRESETS } from "../configs/configsPresets.js"; -import { detectConfigFiles } from "../configs/miscellaneousConfigHelpers.js"; +import { ESLINT_PRESETS } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/configsPresets.js"; +import { detectConfigFiles } from "~/utils/configHandler.js"; +import { relinka } from "~/utils/loggerRelinka.js"; + import { getCurrentDependencies, installDependencies, uninstallDependencies, } from "./dependencies.js"; -import { relinka } from "./logger.js"; export async function handleConfigEditing(cwd: string) { const detectedConfigs = await detectConfigFiles(cwd); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleIntegrations.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleIntegrations.ts index 92bc04aa..ea9d2834 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleIntegrations.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/handleIntegrations.ts @@ -5,10 +5,10 @@ import type { IntegrationCategory, IntegrationOptions } from "~/types.js"; import { installIntegration, removeIntegration, -} from "../integrations/integrations.js"; -import { INTEGRATION_CONFIGS } from "../integrations/integrationsIntegrConfig.js"; -import { REMOVAL_CONFIGS } from "../integrations/integrationsRemovalConfig.js"; -import { relinka } from "./logger.js"; +} from "~/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.js"; +import { INTEGRATION_CONFIGS } from "~/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.js"; +import { REMOVAL_CONFIGS } from "~/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsRemovalConfig.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function handleIntegrations(cwd: string, isDev: boolean) { const integrationOptions: IntegrationOptions = { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/i18nMove.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/i18nMove.ts index b353f24c..87422d1b 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/i18nMove.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/i18nMove.ts @@ -1,7 +1,7 @@ import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Paths that should be ignored when moving files to [locale] const IGNORED_PATHS = ["api"]; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/promptPackageJsonScripts.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/promptPackageJsonScripts.ts index f63bfe3c..453e77ca 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/promptPackageJsonScripts.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/promptPackageJsonScripts.ts @@ -2,9 +2,8 @@ import { msg, multiselectPrompt } from "@reliverse/prompts"; import { execa } from "execa"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - -import { checkScriptExists } from "../../../cp-impl.js"; +import { checkScriptExists } from "~/app/menu/create-project/cp-impl.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export type ScriptStatus = { dbPush: boolean; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/reliverseConfig.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/reliverseConfig.ts deleted file mode 100644 index 6f107e81..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/reliverseConfig.ts +++ /dev/null @@ -1,186 +0,0 @@ -import destr from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { - DeploymentService, - ReliverseConfig, - ReliverseMemory, -} from "~/types.js"; - -import { updateReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - -import { DEFAULT_CONFIG } from "../configs/reliverseDefaultConfig.js"; -import { - getDefaultReliverseConfig, - writeReliverseConfig, -} from "../configs/reliverseReadWrite.js"; - -type GenerateReliverseFileOptions = { - projectName: string; - frontendUsername: string; - deployService: DeploymentService; - primaryDomain: string; - projectPath: string; - i18nShouldBeEnabled: boolean; - shouldInstallDeps: boolean; - isDeployed?: boolean; - overwrite?: boolean; - memory: ReliverseMemory; -}; - -export async function generateReliverseFile({ - projectName, - frontendUsername, - deployService, - primaryDomain, - projectPath, - i18nShouldBeEnabled, - shouldInstallDeps, - overwrite, - memory, -}: GenerateReliverseFileOptions): Promise { - try { - // Read memory to get stored usernames - const githubUsername = memory?.githubUsername ?? ""; - const vercelTeamName = memory?.vercelUsername ?? ""; - - // Load default config rules - const rules = await getDefaultReliverseConfig( - projectName || "my-app", - frontendUsername || "user", - ); - - // Store updated info in memory - await updateReliverseMemory({ - name: frontendUsername, - githubUsername, - vercelUsername: vercelTeamName, - }); - - // Configure 'experimental' block if missing - if (!rules.experimental) { - rules.experimental = {}; - } - - // Attach project details - rules.experimental.projectName = projectName; - rules.experimental.projectAuthor = frontendUsername; - rules.experimental.projectRepository = `https://github.com/${githubUsername}/${projectName}`; - rules.experimental.projectDeployService = deployService; - rules.experimental.projectDomain = primaryDomain - ? `https://${primaryDomain}` - : `https://${projectName}.${deployService.toLowerCase()}.app`; - - // Features - rules.experimental.features = { - ...rules.experimental.features, - i18n: i18nShouldBeEnabled, - authentication: shouldInstallDeps, - database: shouldInstallDeps, - analytics: false, - themeMode: "dark-light", - api: shouldInstallDeps, - testing: true, - docker: false, - ci: true, - commands: [], - webview: [], - language: ["typescript"], - themes: ["default"], - }; - - // Dev preferences - rules.experimental.gitBehavior = "prompt"; - rules.experimental.deployBehavior = "prompt"; - rules.experimental.depsBehavior = "prompt"; - rules.experimental.i18nBehavior = "prompt"; - rules.experimental.scriptsBehavior = "prompt"; - rules.experimental.skipPromptsUseAutoBehavior = false; - - // Prepare a flattened config object merged with DEFAULT_CONFIG - const configContent: ReliverseConfig = { ...DEFAULT_CONFIG }; - - // Flatten relevant fields from rules into configContent - Object.assign(configContent, { - // Project details - projectName: rules.experimental.projectName, - projectAuthor: rules.experimental.projectAuthor, - projectDescription: rules.experimental.projectDescription, - projectVersion: rules.experimental.projectVersion, - projectLicense: rules.experimental.projectLicense, - projectRepository: rules.experimental.projectRepository, - features: rules.experimental.features, - - // Development preferences - projectFramework: rules.experimental.projectFramework, - projectFrameworkVersion: rules.experimental.projectFrameworkVersion, - nodeVersion: rules.experimental.nodeVersion, - runtime: rules.experimental.runtime, - projectPackageManager: rules.experimental.projectPackageManager, - monorepo: rules.experimental.monorepo, - preferredLibraries: rules.experimental.preferredLibraries, - codeStyle: rules.experimental.codeStyle, - }); - - // Define the path for .reliverse - const configPath = path.join(projectPath, ".reliverse"); - - // If overwrite is requested, always write fresh - if (overwrite) { - await writeReliverseConfig(projectPath, configContent); - relinka( - "success-verbose", - "Overwrote existing .reliverse with new config", - ); - return true; - } - - // Otherwise, merge if .reliverse exists or create new - const configExists = await fs.pathExists(configPath); - - if (configExists) { - try { - const existingContent = destr(await fs.readFile(configPath, "utf-8")); - if (existingContent && typeof existingContent === "object") { - const mergedContent = { - ...configContent, - ...(existingContent as ReliverseConfig), - experimental: { - ...(existingContent as ReliverseConfig).experimental, - ...configContent?.experimental, - }, - }; - await writeReliverseConfig(projectPath, mergedContent); - relinka( - "success-verbose", - "Updated existing .reliverse with merged config", - ); - } - } catch (error) { - relinka( - "warn-verbose", - "Error reading existing .reliverse, creating new one", - error instanceof Error ? error.message : String(error), - ); - await writeReliverseConfig(projectPath, configContent); - } - } else { - await writeReliverseConfig(projectPath, configContent); - relinka( - "success-verbose", - "Generated .reliverse with project-specific settings", - ); - } - - return true; - } catch (error) { - relinka( - "error", - "Failed to configure reliverse:", - error instanceof Error ? error.message : String(error), - ); - return false; - } -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/replaceStringsInFiles.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/replaceStringsInFiles.ts index a0a5403b..67b586a2 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/replaceStringsInFiles.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/replaceStringsInFiles.ts @@ -1,7 +1,7 @@ import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function replaceStringsInFiles( projectPath: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/revalidateReliverseJson.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/revalidateReliverseJson.ts deleted file mode 100644 index 3a43037f..00000000 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/revalidateReliverseJson.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { destr } from "destr"; -import fs from "fs-extra"; -import path from "pathe"; - -import type { ReliverseConfig } from "~/types.js"; - -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - -import { generateDefaultRulesForProject } from "../configs/generateDefaultRulesForProject.js"; -import { detectProjectType } from "../configs/miscellaneousConfigHelpers.js"; -import { parseCodeStyleFromConfigs } from "../configs/parseCodeStyleFromConfigs.js"; -import { - getDefaultReliverseConfig, - writeReliverseConfig, -} from "../configs/reliverseReadWrite.js"; - -export async function revalidateReliverseJson(cwd: string, rulesPath: string) { - // Read file content and continue with the rest of the function... - const fileContent = await fs.readFile(rulesPath, "utf-8"); - const parsedContent = fileContent.trim() - ? destr>(fileContent) - : {}; - - // Get default rules based on project type - const projectType = await detectProjectType(cwd); - const defaultRules = projectType - ? await generateDefaultRulesForProject(cwd) - : await getDefaultReliverseConfig( - path.basename(cwd), - "user", - "nextjs", // fallback default - ); - - if (defaultRules) { - // Parse code style from existing config files - const configRules = await parseCodeStyleFromConfigs(cwd); - - // Always merge with defaults to ensure all fields exist - const mergedRules = { - experimental: { - // Start with user's values - ...parsedContent.experimental, - - // Only add defaults for missing fields - projectName: - parsedContent.experimental?.projectName ?? - defaultRules.experimental?.projectName ?? - "", - projectAuthor: - parsedContent.experimental?.projectAuthor ?? - defaultRules.experimental?.projectAuthor ?? - "", - projectDescription: - parsedContent.experimental?.projectDescription ?? - defaultRules.experimental?.projectDescription ?? - "", - projectVersion: - parsedContent.experimental?.projectVersion ?? - defaultRules.experimental?.projectVersion ?? - "0.1.0", - projectLicense: - parsedContent.experimental?.projectLicense ?? - defaultRules.experimental?.projectLicense ?? - "MIT", - projectRepository: - parsedContent.experimental?.projectRepository ?? - defaultRules.experimental?.projectRepository ?? - "", - - // Project features - only merge if missing - features: { - ...defaultRules.experimental?.features, - ...parsedContent.experimental?.features, - }, - - // Development preferences - only set if missing - projectFramework: - parsedContent.experimental?.projectFramework ?? - defaultRules.experimental?.projectFramework, - projectPackageManager: - parsedContent.experimental?.projectPackageManager ?? - defaultRules.experimental?.projectPackageManager, - projectFrameworkVersion: - parsedContent.experimental?.projectFrameworkVersion ?? - defaultRules.experimental?.projectFrameworkVersion, - nodeVersion: - parsedContent.experimental?.nodeVersion ?? - defaultRules.experimental?.nodeVersion, - runtime: - parsedContent.experimental?.runtime ?? - defaultRules.experimental?.runtime, - monorepo: - parsedContent.experimental?.monorepo ?? - defaultRules.experimental?.monorepo, - - // Merge nested objects only if missing - preferredLibraries: { - ...defaultRules.experimental?.preferredLibraries, - ...parsedContent.experimental?.preferredLibraries, - }, - codeStyle: parsedContent.experimental?.codeStyle - ? { - ...defaultRules.experimental?.codeStyle, - ...configRules?.experimental?.codeStyle, - ...parsedContent.experimental?.codeStyle, - } - : undefined, - - // Generation preferences - only set if missing - skipPromptsUseAutoBehavior: - parsedContent.experimental?.skipPromptsUseAutoBehavior ?? - defaultRules.experimental?.skipPromptsUseAutoBehavior ?? - false, - deployBehavior: - parsedContent.experimental?.deployBehavior ?? - defaultRules.experimental?.deployBehavior ?? - "prompt", - depsBehavior: - parsedContent.experimental?.depsBehavior ?? - defaultRules.experimental?.depsBehavior ?? - "prompt", - gitBehavior: - parsedContent.experimental?.gitBehavior ?? - defaultRules.experimental?.gitBehavior ?? - "prompt", - i18nBehavior: - parsedContent.experimental?.i18nBehavior ?? - defaultRules.experimental?.i18nBehavior ?? - "prompt", - scriptsBehavior: - parsedContent.experimental?.scriptsBehavior ?? - defaultRules.experimental?.scriptsBehavior ?? - "prompt", - - // Dependencies management - ignoreDependencies: - parsedContent.experimental?.ignoreDependencies ?? - defaultRules.experimental?.ignoreDependencies, - - // Custom rules - customRules: { - ...defaultRules.experimental?.customRules, - ...parsedContent.experimental?.customRules, - }, - }, - }; - - // Only write if there were missing fields or different values - const currentContent = JSON.stringify(mergedRules); - const originalContent = JSON.stringify(parsedContent); - - if (currentContent !== originalContent) { - const hasNewFields = Object.keys(mergedRules.experimental ?? {}).some( - (key) => { - const mergedValue = JSON.stringify( - mergedRules.experimental?.[ - key as keyof NonNullable - ], - ); - const parsedValue = JSON.stringify( - parsedContent.experimental?.[ - key as keyof NonNullable - ], - ); - return mergedValue !== parsedValue; - }, - ); - - if (hasNewFields) { - await writeReliverseConfig(cwd, mergedRules); - relinka( - "info", - "Updated .reliverse with missing configurations. Please review and adjust as needed.", - ); - } - } - } -} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/shadcn.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/shadcn.ts index 726302df..6efcb610 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/shadcn.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/shadcn.ts @@ -4,8 +4,8 @@ import path from "pathe"; import type { ShadcnConfig, Theme } from "~/types.js"; -import { pmx } from "../detections/detectPackageManager.js"; -import { relinka } from "./logger.js"; +import { pmx } from "~/app/menu/create-project/cp-modules/cli-main-modules/detections/detectPackageManager.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const COMPONENT_DEPENDENCIES: Record = { "alert-dialog": ["button"], diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.ts index 7c51b8de..567f473d 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import { cwd } from "node:process"; import { normalize } from "pathe"; -import { relinka } from "./logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export const handleError = (error: unknown) => error instanceof Error ? error.message : "Unknown error"; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.ts index 7d3ebd22..7e446d29 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.ts @@ -1,4 +1,4 @@ -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; function handlePromptCancellation(input: unknown, exitMessage: string): void { if (typeof input === "symbol" && String(input) === "Symbol(clack:cancel)") { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.ts index 35ef585f..e875e01c 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrations.ts @@ -4,7 +4,7 @@ import path from "pathe"; import type { IntegrationConfig, RemovalConfig } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; enum PackageManager { Bun = "bun", diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.ts index fbe94dfe..e90806af 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/integrations/integrationsIntegrConfig.ts @@ -2,7 +2,7 @@ import { execa } from "execa"; import type { IntegrationConfig } from "~/types.js"; -import { relinka } from "../handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; // Integration configurations export const INTEGRATION_CONFIGS: Record = { diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askCodemodUserCodebase.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askCodemodUserCodebase.ts index 54616301..376923a0 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askCodemodUserCodebase.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askCodemodUserCodebase.ts @@ -1,9 +1,8 @@ import { selectPrompt, inputPrompt } from "@reliverse/prompts"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { replaceImportSymbol } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.js"; import { validate } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.js"; - -import { replaceImportSymbol } from "../handlers/codemods/replaceImportSymbol.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function askCodemodUserCodebase(cwd: string) { relinka("info", "The code modification process will start now."); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.ts index 0b7d9bde..9cc6f028 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.ts @@ -2,8 +2,8 @@ import { inputPrompt } from "@reliverse/prompts"; import type { ReliverseMemory } from "~/types.js"; -import { updateReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { updateReliverseMemory } from "~/utils/reliverseMemory.js"; export async function askGithubName( memory: ReliverseMemory, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askSummaryConfirmation.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askSummaryConfirmation.ts index 331b407c..8f3cb93d 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askSummaryConfirmation.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askSummaryConfirmation.ts @@ -1,8 +1,8 @@ import { confirmPrompt } from "@reliverse/prompts"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { validate } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function askSummaryConfirmation( template: string, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askToResolveProjectConflicts.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askToResolveProjectConflicts.ts index 0082a25b..3b99d901 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askToResolveProjectConflicts.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askToResolveProjectConflicts.ts @@ -9,7 +9,7 @@ import { removeFile, renameFile, } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/fileUtils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export const resolveProjectConflicts = async (projectPath: string) => { // Ask user if they want to decide what to do with each file conflict diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askUserName.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askUserName.ts index acd5b793..362b740e 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askUserName.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askUserName.ts @@ -3,8 +3,8 @@ import pc from "picocolors"; import type { ReliverseMemory } from "~/types.js"; -import { updateReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { updateReliverseMemory } from "~/utils/reliverseMemory.js"; const DEFAULT_NAME = "johnny911"; diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askVercelName.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askVercelName.ts index cf0759a6..448be4e6 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askVercelName.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/askVercelName.ts @@ -2,7 +2,7 @@ import { inputPrompt } from "@reliverse/prompts"; import type { ReliverseMemory } from "~/types.js"; -import { updateReliverseMemory } from "~/app/app-utils.js"; +import { updateReliverseMemory } from "~/utils/reliverseMemory.js"; export async function askVercelName( memory: ReliverseMemory, diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showRelivatorFeatEditor.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showRelivatorFeatEditor.ts index dc95b183..833eb768 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showRelivatorFeatEditor.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showRelivatorFeatEditor.ts @@ -1,6 +1,6 @@ import { selectPrompt } from "@reliverse/prompts"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function showRelivatorFeatEditor() { relinka("info", "Relivator feature editor"); diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showStartEndPrompt.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showStartEndPrompt.ts index 4858cfcc..22564856 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showStartEndPrompt.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showStartEndPrompt.ts @@ -6,7 +6,7 @@ export async function showStartPrompt(isDev: boolean) { titleColor: "inverse", clearConsole: true, packageName: "@reliverse/cli", - packageVersion: "1.4.19", + packageVersion: "1.4.20", isDev, }); } diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showUpdateCloneMenu.ts b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showUpdateCloneMenu.ts index 66583243..488d0830 100644 --- a/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showUpdateCloneMenu.ts +++ b/src/app/menu/create-project/cp-modules/cli-main-modules/modules/showUpdateCloneMenu.ts @@ -4,11 +4,10 @@ import { ofetch } from "ofetch"; import path from "pathe"; import { getRepoUrl } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { downloadGitRepo } from "~/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadGitRepo.js"; +import { replaceImportSymbol } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/codemods/replaceImportSymbol.js"; import { validate } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/validate.js"; - -import { downloadGitRepo } from "../downloads/downloadGitRepo.js"; -import { replaceImportSymbol } from "../handlers/codemods/replaceImportSymbol.js"; +import { relinka } from "~/utils/loggerRelinka.js"; type UpdateConfig = { actions: { diff --git a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env-manager.ts b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env-manager.ts index 52ebc9a7..ffef2a86 100644 --- a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env-manager.ts +++ b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env-manager.ts @@ -1,7 +1,7 @@ import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import type { KeyVars } from "./keys.js"; diff --git a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env.ts b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env.ts index 0a35207f..632c40d1 100644 --- a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env.ts +++ b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/env.ts @@ -7,7 +7,7 @@ import path from "pathe"; import { db } from "~/app/db/client.js"; import { encrypt, decrypt } from "~/app/db/config.js"; import { userDataTable } from "~/app/db/schema.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import type { KeyVars } from "./keys.js"; diff --git a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/file.ts b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/file.ts index df5098ea..f59a27ba 100644 --- a/src/app/menu/create-project/cp-modules/compose-env-file/helpers/file.ts +++ b/src/app/menu/create-project/cp-modules/compose-env-file/helpers/file.ts @@ -1,6 +1,6 @@ import { ofetch } from "ofetch"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function fetchEnvExampleContent( urlResource: string, diff --git a/src/app/menu/create-project/cp-modules/compose-env-file/mod.ts b/src/app/menu/create-project/cp-modules/compose-env-file/mod.ts index 528e5878..229187c3 100644 --- a/src/app/menu/create-project/cp-modules/compose-env-file/mod.ts +++ b/src/app/menu/create-project/cp-modules/compose-env-file/mod.ts @@ -3,7 +3,7 @@ import { execa } from "execa"; import fs from "fs-extra"; import open from "open"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { ensureExampleExists, diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/deploy.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/deploy.ts index 03af25c2..1b284207 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/deploy.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/deploy.ts @@ -1,13 +1,10 @@ import { selectPrompt } from "@reliverse/prompts"; import pc from "picocolors"; -import type { - DeploymentService, - ReliverseConfig, - ReliverseMemory, -} from "~/types.js"; +import type { DeploymentService, ReliverseMemory } from "~/types.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { createVercelDeployment } from "./vercel/vercel-mod.js"; @@ -15,10 +12,10 @@ export async function selectDeploymentService( config: ReliverseConfig, ): Promise { if ( - config?.experimental?.projectDeployService && - config.experimental.projectDeployService !== "none" + config.projectDeployService !== undefined && + config.projectDeployService !== "none" ) { - const deployService = config.experimental.projectDeployService; + const deployService = config.projectDeployService; relinka("info", `Using configured deployment service: ${deployService}`); return deployService; } diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/git.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/git.ts index 19f0b086..1dfeed0d 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/git.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/git.ts @@ -8,12 +8,12 @@ import { simpleGit } from "simple-git"; import type { ReliverseMemory } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { cd, pwd, } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.js"; import { askGithubName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { checkGithubRepoOwnership, createGithubRepo } from "./github.js"; import { createOctokitInstance } from "./octokit-instance.js"; diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/github.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/github.ts index 148bd509..f27d6fb9 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/github.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/github.ts @@ -7,10 +7,10 @@ import path from "pathe"; import type { ReliverseMemory } from "~/types.js"; -import { updateReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { cd } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { updateReliverseMemory } from "~/utils/reliverseMemory.js"; -import { cd } from "../cli-main-modules/handlers/terminal.js"; import { createOctokitInstance } from "./octokit-instance.js"; import { cloneToTempAndCopyFiles, setupGitRemote } from "./utils-git-github.js"; diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/helpers/handlePkgJsonScripts.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/helpers/handlePkgJsonScripts.ts index 97037f24..05a9b7a4 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/helpers/handlePkgJsonScripts.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/helpers/handlePkgJsonScripts.ts @@ -2,7 +2,7 @@ import { selectPrompt } from "@reliverse/prompts"; import { execa } from "execa"; import { installDependencies } from "nypm"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function ensureDbInitialized( hasDbPush: boolean, diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/mod.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/mod.ts index 45cc9e69..3f7302a2 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/mod.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/mod.ts @@ -2,15 +2,12 @@ import type { Octokit } from "@octokit/rest"; import { confirmPrompt, selectPrompt } from "@reliverse/prompts"; -import type { - DeploymentService, - ReliverseConfig, - ReliverseMemory, -} from "~/types.js"; - -import { getReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import type { DeploymentService, ReliverseMemory } from "~/types.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; + import { askGithubName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { getReliverseMemory } from "~/utils/reliverseMemory.js"; import { deployProject } from "./deploy.js"; import { createGithubRepository, initGit } from "./git.js"; @@ -39,7 +36,7 @@ export async function decide( defaultValue = true, ): Promise { try { - const behavior = config?.experimental?.[behaviorKey] ?? "prompt"; + const behavior = config?.[behaviorKey] ?? "prompt"; switch (behavior) { case "autoYes": diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.ts index 752f9428..cfc33de8 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.ts @@ -2,7 +2,7 @@ import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods"; import { Octokit } from "@octokit/rest"; export const OctokitWithRest = Octokit.plugin(restEndpointMethods); -export const octokitUserAgent = "reliverse-cli/1.4.19"; +export const octokitUserAgent = "reliverse-cli/1.4.20"; // https://github.com/octokit/octokit.js/#readme export function createOctokitInstance( diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/utils-git-github.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/utils-git-github.ts index b888768a..a199ae8d 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/utils-git-github.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/utils-git-github.ts @@ -1,3 +1,5 @@ +import type { PackageJson } from "pkg-types"; + import { confirmPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import { createTarGzip } from "nanotar"; @@ -5,7 +7,9 @@ import { homedir } from "os"; import path from "pathe"; import { simpleGit } from "simple-git"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import type { ReliverseMemory } from "~/types.js"; + +import { relinka } from "~/utils/loggerRelinka.js"; type FileInput = { name: string; @@ -282,3 +286,25 @@ export async function setupGitRemote( return false; } } + +export function extractGithubUsername( + packageJson: PackageJson | null, + memory: ReliverseMemory, + fallback: string, +): string { + let githubUsername = memory.githubUsername; + if (!githubUsername && packageJson?.repository) { + const repoUrl = + typeof packageJson.repository === "string" + ? packageJson.repository + : packageJson.repository.url; + + if (repoUrl) { + const match = /github\.com[:/]([^/]+)/.exec(repoUrl); + if (match) { + githubUsername = match[1]; + } + } + } + return githubUsername ?? fallback; +} diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-api.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-api.ts index e509a8a5..83f2bd66 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-api.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-api.ts @@ -1,6 +1,6 @@ import type { Vercel } from "@vercel/sdk"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import type { EnvVar, VercelEnvResponse } from "./vercel-types.js"; diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-config.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-config.ts index 1c18a697..1174f3a3 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-config.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-config.ts @@ -3,7 +3,7 @@ import type { Vercel } from "@vercel/sdk"; import { multiselectPrompt } from "@reliverse/prompts"; import { experimental } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; /** * Enables analytics for the project diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-deploy.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-deploy.ts index 6652dad8..6313f46e 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-deploy.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-deploy.ts @@ -4,7 +4,7 @@ import { type InlinedFile } from "@vercel/sdk/models/createdeploymentop.js"; import fs from "fs-extra"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import type { DeploymentLog, diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-domain.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-domain.ts index 8e88e2db..4473b7cf 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-domain.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-domain.ts @@ -3,7 +3,7 @@ import { projectsGetProjectDomains } from "@vercel/sdk/funcs/projectsGetProjectD import type { ReliverseMemory } from "~/types.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { createVercelCoreInstance } from "./vercel-instance.js"; diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-env.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-env.ts index 4102c779..8ac41509 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-env.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-env.ts @@ -3,7 +3,7 @@ import type { Vercel } from "@vercel/sdk"; import { confirmPrompt } from "@reliverse/prompts"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import type { DeploymentOptions, EnvVar } from "./vercel-types.js"; diff --git a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-mod.ts b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-mod.ts index 61539903..79ff22a0 100644 --- a/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-mod.ts +++ b/src/app/menu/create-project/cp-modules/git-deploy-prompts/vercel/vercel-mod.ts @@ -10,11 +10,11 @@ import path from "pathe"; import type { ReliverseMemory } from "~/types.js"; -import { updateReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { askGithubName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askGithubName.js"; +import { createOctokitInstance } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/octokit-instance.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { updateReliverseMemory } from "~/utils/reliverseMemory.js"; -import { createOctokitInstance } from "../octokit-instance.js"; import { withRateLimit } from "./vercel-api.js"; import { enableAnalytics, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/createProject.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/createProject.ts index 2b8b77c9..e50b7399 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/createProject.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/createProject.ts @@ -2,11 +2,14 @@ import fs from "fs-extra"; import { globby } from "globby"; import path from "pathe"; -import { PKG_ROOT } from "~/app/constants.js"; +import type { + DatabaseProvider, + PkgInstallerMap, +} from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; -import type { DatabaseProvider, PkgInstallerMap } from "../opts.js"; +import { PKG_ROOT } from "~/app/constants.js"; +import { getUserPkgManager } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/getUserPkgManager.js"; -import { getUserPkgManager } from "../utils/getUserPkgManager.js"; import { installPackages } from "./installPackages.js"; import { scaffoldProject } from "./scaffoldProject.js"; import { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/git.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/git.ts index c9012fee..ee4345d6 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/git.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/git.ts @@ -6,7 +6,7 @@ import ora from "ora"; import path from "pathe"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const isGitInstalled = (dir: string): boolean => { try { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installDependencies.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installDependencies.ts index 4dfeb83e..22312357 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installDependencies.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installDependencies.ts @@ -2,12 +2,11 @@ import { execa } from "execa"; import ora, { type Ora } from "ora"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; - import { getUserPkgManager, type PackageManager, -} from "../utils/getUserPkgManager.js"; +} from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/getUserPkgManager.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const execWithSpinner = async ( projectDir: string, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installPackages.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installPackages.ts index cb4ebd69..bec46181 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installPackages.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/installPackages.ts @@ -2,10 +2,10 @@ import { execa } from "execa"; import ora from "ora"; import pc from "picocolors"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import type { PkgInstallerMap } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import type { InstallerOptions } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; -import type { PkgInstallerMap } from "../opts.js"; -import type { InstallerOptions } from "../opts.js"; +import { relinka } from "~/utils/loggerRelinka.js"; type InstallPackagesOptions = InstallerOptions & { packages: PkgInstallerMap; diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/logNextSteps.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/logNextSteps.ts index 40b35ab9..6c0344d0 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/logNextSteps.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/logNextSteps.ts @@ -1,8 +1,8 @@ import { DEFAULT_APP_NAME } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { type InstallerOptions } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { getUserPkgManager } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/getUserPkgManager.js"; +import { relinka } from "~/utils/loggerRelinka.js"; -import { type InstallerOptions } from "../opts.js"; -import { getUserPkgManager } from "../utils/getUserPkgManager.js"; import { isInsideGitRepo, isRootGitRepo } from "./git.js"; // This logs the next steps that the user should take in order to advance the project diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/scaffoldProject.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/scaffoldProject.ts index 77d452c3..3e3231b7 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/scaffoldProject.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/scaffoldProject.ts @@ -5,10 +5,10 @@ import ora from "ora"; import path from "pathe"; import pc from "picocolors"; -import { PKG_ROOT } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import type { InstallerOptions } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; -import type { InstallerOptions } from "../opts.js"; +import { PKG_ROOT } from "~/app/constants.js"; +import { relinka } from "~/utils/loggerRelinka.js"; /** * Renames all -tsx.txt files back to .tsx in the specified directory and its subdirectories. diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/selectBoilerplate.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/selectBoilerplate.ts index d04193c1..aacfadfa 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/selectBoilerplate.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/helpers/selectBoilerplate.ts @@ -2,8 +2,7 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type InstallerOptions } from "../opts.js"; +import { type InstallerOptions } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; type SelectBoilerplateProps = Required< Pick diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/impl.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/impl.ts index e42ffe8a..46f62dbc 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/impl.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/impl.ts @@ -2,7 +2,7 @@ import * as p from "@clack/prompts"; import pc from "picocolors"; import { CREATE_RELIVERSE_APP } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { defaultOptions, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/dbContainer.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/dbContainer.ts index 90289bd8..5bca68e0 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/dbContainer.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/dbContainer.ts @@ -2,9 +2,8 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type Installer } from "../opts.js"; -import { parseNameAndPath } from "../utils/parseNameAndPath.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { parseNameAndPath } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/parseNameAndPath.js"; // Sanitizes a project name to ensure it adheres to Docker container naming conventions. const sanitizeName = (name: string): string => { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/drizzle.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/drizzle.ts index 3fa3d08c..a5b57986 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/drizzle.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/drizzle.ts @@ -4,9 +4,9 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { addPackageDependency } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.js"; -import { type Installer } from "../opts.js"; -import { addPackageDependency } from "../utils/addPackageDependency.js"; import { type AvailableDependencies } from "./dependencyVersionMap.js"; export const drizzleInstaller: Installer = ({ diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/envVars.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/envVars.ts index 1fd99c14..94ae2aaa 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/envVars.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/envVars.ts @@ -3,8 +3,10 @@ import crypto from "node:crypto"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type DatabaseProvider, type Installer } from "../opts.js"; +import { + type DatabaseProvider, + type Installer, +} from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; export const envVariablesInstaller: Installer = ({ projectDir, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/nextAuth.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/nextAuth.ts index 3a6a335d..77d50f5a 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/nextAuth.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/nextAuth.ts @@ -2,9 +2,9 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { addPackageDependency } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.js"; -import { type Installer } from "../opts.js"; -import { addPackageDependency } from "../utils/addPackageDependency.js"; import { type AvailableDependencies } from "./dependencyVersionMap.js"; export const nextAuthInstaller: Installer = ({ projectDir, packages }) => { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/prisma.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/prisma.ts index 4d8a2658..ca0aba25 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/prisma.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/prisma.ts @@ -4,9 +4,8 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type Installer } from "../opts.js"; -import { addPackageDependency } from "../utils/addPackageDependency.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { addPackageDependency } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.js"; export const prismaInstaller: Installer = ({ projectDir, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/tailwind.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/tailwind.ts index 7eccd8cf..c6354feb 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/tailwind.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/tailwind.ts @@ -4,9 +4,8 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type Installer } from "../opts.js"; -import { addPackageDependency } from "../utils/addPackageDependency.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { addPackageDependency } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.js"; export const tailwindInstaller: Installer = ({ projectDir }) => { addPackageDependency({ diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/trpc.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/trpc.ts index 79d7a5c1..2539844e 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/installers/trpc.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/installers/trpc.ts @@ -2,9 +2,8 @@ import fs from "fs-extra"; import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; - -import { type Installer } from "../opts.js"; -import { addPackageDependency } from "../utils/addPackageDependency.js"; +import { type Installer } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import { addPackageDependency } from "~/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.js"; export const trpcInstaller: Installer = ({ projectDir, diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post-tw.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post-tw.tsx index 522bd0e4..1f3c33b8 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post-tw.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post-tw.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; -import { api } from "../../utils/api.js"; +import { api } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; export function LatestPost() { const [latestPost] = api.post.getLatest.useSuspenseQuery(); diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post.tsx index 7af9597a..d1d8cf14 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/_components/post.tsx @@ -2,8 +2,8 @@ import { useState } from "react"; -import { api } from "../../utils/api.js"; -import styles from "../index.module.css"; +import styles from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/index.module.css"; +import { api } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; export function LatestPost() { const [latestPost] = api.post.getLatest.useSuspenseQuery(); diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/api/auth/[...nextauth]/route.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/api/auth/[...nextauth]/route.ts index ece7247e..692212a2 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/api/auth/[...nextauth]/route.ts @@ -1,3 +1,3 @@ -import { handlers } from "../../../../server/auth/index.js"; +import { handlers } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/auth/index.js"; export const { GET, POST } = handlers; diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/layout/with-trpc-tw.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/layout/with-trpc-tw.tsx index 90aded7b..7374624c 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/layout/with-trpc-tw.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/app/layout/with-trpc-tw.tsx @@ -4,7 +4,7 @@ import "~/styles/globals.css"; import React from "react"; -import { TRPCReactProvider } from "../../trpc/react.jsx"; +import { TRPCReactProvider } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/react.jsx"; export const metadata: Metadata = { title: "@reliverse/cli", diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-auth-trpc.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-auth-trpc.tsx index 793a8fb6..a1b9eac0 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-auth-trpc.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-auth-trpc.tsx @@ -3,8 +3,7 @@ import { SessionProvider } from "next-auth/react"; import React from "react"; import { Outlet } from "react-router-dom"; -import { queryClient } from "../../utils/api.js"; - +import { queryClient } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; import "~/styles/globals.css"; export default function AuthTrpcLayout() { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc-tw.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc-tw.tsx index 823b91b9..97cceca0 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc-tw.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc-tw.tsx @@ -2,8 +2,7 @@ import { QueryClientProvider } from "@tanstack/react-query"; import React from "react"; import { Outlet } from "react-router-dom"; -import { queryClient } from "../../utils/api.js"; - +import { queryClient } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; import "~/styles/globals.css"; export default function TrpcLayout() { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc.tsx index ba449098..ba01d60e 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/_app/with-trpc.tsx @@ -2,8 +2,7 @@ import { QueryClientProvider } from "@tanstack/react-query"; import React from "react"; import { Outlet } from "react-router-dom"; -import { queryClient } from "../../utils/api.js"; - +import { queryClient } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; import "~/styles/globals.css"; export default function TrpcLayout() { diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/index/with-auth-trpc-tw.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/index/with-auth-trpc-tw.tsx index c1323827..01d263b5 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/index/with-auth-trpc-tw.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/pages/index/with-auth-trpc-tw.tsx @@ -5,7 +5,7 @@ import Head from "next/head"; import Link from "next/link"; import React from "react"; -import { api } from "../../utils/api.js"; +import { api } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.js"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/routers/post.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/routers/post.ts index ea02ef7e..f5ee2ee7 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/routers/post.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/routers/post.ts @@ -4,7 +4,7 @@ import { createTRPCRouter, publicProcedure, protectedProcedure, -} from "../trpc.js"; +} from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/trpc.js"; export const postRouter = createTRPCRouter({ hello: publicProcedure diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/react.tsx b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/react.tsx index 6d15b6d0..48659a5b 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/react.tsx +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/react.tsx @@ -8,7 +8,8 @@ import { useState } from "react"; import React from "react"; import SuperJSON from "superjson"; -import { type AppRouter } from "../server/api/root.js"; +import { type AppRouter } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/root.js"; + import { createQueryClient } from "./query-client.js"; let clientQueryClientSingleton: QueryClient | undefined = undefined; diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/server.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/server.ts index a8dac96b..d1550d33 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/server.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/trpc/server.ts @@ -4,8 +4,9 @@ import { createHydrationHelpers } from "@trpc/react-query/rsc"; import { headers } from "next/headers"; import { cache } from "react"; -import { type AppRouter } from "../server/api/root.js"; -import { createTRPCContext } from "../server/api/trpc.js"; +import { type AppRouter } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/root.js"; +import { createTRPCContext } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/trpc.js"; + import { createQueryClient } from "./query-client.js"; /** diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.ts index 7b4a0746..90296f3a 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/utils/api.ts @@ -10,7 +10,7 @@ import { createTRPCReact } from "@trpc/react-query"; import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server"; import SuperJSON from "superjson"; -import type { AppRouter } from "../server/api/root.js"; +import type { AppRouter } from "~/app/menu/create-project/cp-modules/use-composer-mode/template/extras/src/server/api/root.js"; export const api = createTRPCReact(); diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.ts index 21d88e68..93c38def 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/addPackageDependency.ts @@ -4,9 +4,9 @@ import fs from "fs-extra"; import path from "pathe"; import sortPackageJson from "sort-package-json"; -import type { AvailableDependencies } from "../installers/dependencyVersionMap.js"; +import type { AvailableDependencies } from "~/app/menu/create-project/cp-modules/use-composer-mode/installers/dependencyVersionMap.js"; -import { dependencyVersionMap } from "../installers/dependencyVersionMap.js"; +import { dependencyVersionMap } from "~/app/menu/create-project/cp-modules/use-composer-mode/installers/dependencyVersionMap.js"; export const addPackageDependency = (opts: { dependencies: AvailableDependencies[]; diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/getReliverseCliVersion.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/getCliVersion.ts similarity index 90% rename from src/app/menu/create-project/cp-modules/use-composer-mode/utils/getReliverseCliVersion.ts rename to src/app/menu/create-project/cp-modules/use-composer-mode/utils/getCliVersion.ts index 69136c92..fcc620fe 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/getReliverseCliVersion.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/getCliVersion.ts @@ -5,7 +5,7 @@ import path from "pathe"; import { PKG_ROOT } from "~/app/constants.js"; -export const getVersion = () => { +export const getCliVersion = () => { const packageJsonPath = path.join(PKG_ROOT, "package.json"); const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson; diff --git a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/renderVersionWarning.ts b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/renderVersionWarning.ts index 72c8499a..00f37687 100644 --- a/src/app/menu/create-project/cp-modules/use-composer-mode/utils/renderVersionWarning.ts +++ b/src/app/menu/create-project/cp-modules/use-composer-mode/utils/renderVersionWarning.ts @@ -1,12 +1,12 @@ import { execSync } from "child_process"; import https from "https"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; -import { getVersion } from "./getReliverseCliVersion.js"; +import { getCliVersion } from "./getCliVersion.js"; export const renderVersionWarning = (npmVersion: string) => { - const currentVersion = getVersion(); + const currentVersion = getCliVersion(); // console.log("current", currentVersion); // console.log("npm", npmVersion); diff --git a/src/app/menu/menu-mod.ts b/src/app/menu/menu-mod.ts index ca4b6506..5fbd075c 100644 --- a/src/app/menu/menu-mod.ts +++ b/src/app/menu/menu-mod.ts @@ -6,26 +6,23 @@ import { import pc from "picocolors"; import type { CliResults } from "~/app/menu/create-project/cp-modules/use-composer-mode/opts.js"; +import type { ReliverseConfig } from "~/utils/reliverseConfig.js"; import { DEFAULT_APP_NAME, experimental, recommended, } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; -import { showComposerMode } from "~/app/menu/create-project/cp-modules/use-composer-mode/mod.js"; -import { - type ReliverseConfig, - type ReliverseMemory, - type TemplateOption, -} from "~/types.js"; - import { randomProjectFrameworkTitle, randomInitialMessage, randomWebsiteCategoryTitle, randomWebsiteDetailsTitle, -} from "../db/messages.js"; +} from "~/app/db/messages.js"; +import { showComposerMode } from "~/app/menu/create-project/cp-modules/use-composer-mode/mod.js"; +import { type ReliverseMemory, type TemplateOption } from "~/types.js"; +import { relinka } from "~/utils/loggerRelinka.js"; + import { createWebProject } from "./create-project/cp-mod.js"; const TEMPLATE_OPTIONS = { @@ -233,31 +230,25 @@ export async function buildBrandNewThing( i18nShouldBeEnabled: true, isDev, config: config ?? { - experimental: { - projectDisplayName: extensionConfig.displayName, - projectDescription: extensionConfig.description, - features: { - commands: extensionConfig.features.includes("commands") - ? ["*"] - : [], - webview: extensionConfig.features.includes("webview") ? ["*"] : [], - language: extensionConfig.features.includes("language") - ? ["*"] - : [], - themes: extensionConfig.features.includes("themes") ? ["*"] : [], - i18n: false, - analytics: false, - themeMode: "dark-light", - authentication: false, - api: false, - database: false, - testing: false, - docker: false, - ci: false, - }, - projectActivation: extensionConfig.activation, - projectAuthor: extensionConfig.publisher, + projectDisplayName: extensionConfig.displayName, + projectDescription: extensionConfig.description, + features: { + commands: extensionConfig.features.includes("commands") ? ["*"] : [], + webview: extensionConfig.features.includes("webview") ? ["*"] : [], + language: extensionConfig.features.includes("language") ? ["*"] : [], + themes: extensionConfig.features.includes("themes") ? ["*"] : [], + i18n: false, + analytics: false, + themeMode: "dark-light", + authentication: false, + api: false, + database: false, + testing: false, + docker: false, + ci: false, }, + projectActivation: extensionConfig.activation, + projectAuthor: extensionConfig.publisher, }, memory, cwd, @@ -266,7 +257,7 @@ export async function buildBrandNewThing( } // Get projectFramework from config or prompt for web applications - let projectFramework = config?.experimental?.projectFramework; + let projectFramework = config?.projectFramework; if (!projectFramework) { const result = await selectPrompt({ endTitle, @@ -355,8 +346,8 @@ export async function buildBrandNewThing( // Get template from config or prompt let template: TemplateOption; - if (config?.experimental?.projectTemplate) { - template = config.experimental.projectTemplate; + if (config?.projectTemplate) { + template = config.projectTemplate; } else { const result = await selectPrompt({ endTitle, @@ -381,13 +372,11 @@ export async function buildBrandNewThing( Math.floor(Math.random() * randomWebsiteDetailsTitle.length) ]!, mode: "buildBrandNewThing", - i18nShouldBeEnabled: config?.experimental?.i18nBehavior === "prompt", + i18nShouldBeEnabled: config?.i18nBehavior === "prompt", isDev, config: config ?? { - experimental: { - i18nBehavior: "prompt", - projectFramework: "nextjs", - }, + i18nBehavior: "prompt", + projectFramework: "nextjs", }, memory, cwd, diff --git a/src/args/config/mod.ts b/src/args/config/mod.ts index 7d3ed4dd..6c40c961 100644 --- a/src/args/config/mod.ts +++ b/src/args/config/mod.ts @@ -2,11 +2,13 @@ import { defineCommand } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import { DEFAULT_CONFIG } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseDefaultConfig.js"; -import { getDefaultReliverseConfig } from "~/app/menu/create-project/cp-modules/cli-main-modules/configs/reliverseReadWrite.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { getCurrentWorkingDirectory } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/terminal.js"; -import { type ReliverseConfig } from "~/types.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { + DEFAULT_CONFIG, + getDefaultReliverseConfig, + type ReliverseConfig, +} from "~/utils/reliverseConfig.js"; export default defineCommand({ meta: { @@ -34,149 +36,146 @@ export default defineCommand({ process.exit(1); } - const rules = await getDefaultReliverseConfig("my-app", "user"); + const rules = await getDefaultReliverseConfig( + cwd, + "my-app", + "user", + "nextjs", + ); // Create a config that includes both rules and legacy config fields const config: ReliverseConfig = { - experimental: { - // Project details - projectName: rules.experimental?.projectName ?? "my-app", - projectAuthor: rules.experimental?.projectAuthor ?? "user", - projectDescription: rules.experimental?.projectDescription ?? "", - projectVersion: rules.experimental?.projectVersion ?? "0.1.0", - projectLicense: rules.experimental?.projectLicense ?? "MIT", - projectRepository: rules.experimental?.projectRepository ?? "", - projectActivation: rules.experimental?.projectActivation ?? "auto", - projectCategory: rules.experimental?.projectCategory ?? "website", - projectType: rules.experimental?.projectType ?? "library", - projectDeployService: - rules.experimental?.projectDeployService ?? "vercel", - projectDisplayName: rules.experimental?.projectDisplayName ?? "", - projectDomain: rules.experimental?.projectDomain ?? "", - projectState: rules.experimental?.projectState ?? "creating", - projectSubcategory: - rules.experimental?.projectSubcategory ?? "e-commerce", - projectTemplate: - rules.experimental?.projectTemplate ?? "blefnk/relivator", - projectFramework: rules.experimental?.projectFramework ?? "nextjs", - projectFrameworkVersion: - rules.experimental?.projectFrameworkVersion ?? "latest", - projectPackageManager: - rules.experimental?.projectPackageManager ?? "npm", - nodeVersion: rules.experimental?.nodeVersion ?? "latest", - runtime: rules.experimental?.runtime ?? "nodejs", - productionBranch: rules.experimental?.productionBranch ?? "main", - deployUrl: rules.experimental?.deployUrl ?? "", + // Project details + projectName: rules.projectName ?? "my-app", + projectAuthor: rules.projectAuthor ?? "user", + projectDescription: rules.projectDescription ?? "", + projectVersion: rules.projectVersion ?? "0.1.0", + projectLicense: rules.projectLicense ?? "MIT", + projectRepository: rules.projectRepository ?? "", + projectActivation: rules.projectActivation ?? "auto", + projectCategory: rules.projectCategory ?? "website", + projectType: rules.projectType ?? "library", + projectDeployService: rules.projectDeployService ?? "vercel", + projectDisplayName: rules.projectDisplayName ?? "", + projectDomain: rules.projectDomain ?? "", + projectState: rules.projectState ?? "creating", + projectSubcategory: rules.projectSubcategory ?? "e-commerce", + projectTemplate: rules.projectTemplate ?? "blefnk/relivator", + projectFramework: rules.projectFramework ?? "nextjs", + projectFrameworkVersion: rules.projectFrameworkVersion ?? "latest", + projectPackageManager: rules.projectPackageManager ?? "bun", + nodeVersion: rules.nodeVersion ?? "latest", + runtime: rules.runtime ?? "nodejs", + productionBranch: rules.productionBranch ?? "main", + deployUrl: rules.deployUrl ?? "", - // Development preferences - monorepo: rules.experimental?.monorepo ?? { - type: "turborepo", - packages: [], - sharedPackages: [], - }, + // Development preferences + monorepo: rules.monorepo ?? { + type: "turborepo", + packages: [], + sharedPackages: [], + }, - preferredLibraries: { - stateManagement: "zustand", - styling: "tailwind", - database: "drizzle", - testing: "vitest", - linting: "eslint", - formatting: "biome", - deployment: "vercel", - authentication: "clerk", - payment: "stripe", - analytics: "vercel", - formManagement: "react-hook-form", - uiComponents: "shadcn-ui", - monitoring: "sentry", - logging: "axiom", - forms: "react-hook-form", - validation: "zod", - documentation: "starlight", - components: "shadcn", - icons: "lucide", - mail: "resend", - search: "algolia", - cache: "redis", - storage: "cloudflare", - cdn: "cloudflare", - api: "trpc", - cms: "contentlayer", - i18n: "next-intl", - seo: "next-seo", - ui: "radix", - motion: "framer", - charts: "recharts", - dates: "dayjs", - markdown: "mdx", - security: "auth", - notifications: "sonner", - uploads: "uploadthing", - routing: "next", - ...rules.experimental?.preferredLibraries, - }, + preferredLibraries: { + stateManagement: "zustand", + styling: "tailwind", + database: "drizzle", + testing: "vitest", + linting: "eslint", + formatting: "biome", + deployment: "vercel", + authentication: "clerk", + payment: "stripe", + analytics: "vercel", + formManagement: "react-hook-form", + uiComponents: "shadcn-ui", + monitoring: "sentry", + logging: "axiom", + forms: "react-hook-form", + validation: "zod", + documentation: "starlight", + components: "shadcn", + icons: "lucide", + mail: "resend", + search: "algolia", + cache: "redis", + storage: "cloudflare", + cdn: "cloudflare", + api: "trpc", + cms: "contentlayer", + i18n: "next-intl", + seo: "next-seo", + ui: "radix", + motion: "framer", + charts: "recharts", + dates: "dayjs", + markdown: "mdx", + security: "auth", + notifications: "sonner", + uploads: "uploadthing", + routing: "next", + ...rules.preferredLibraries, + }, - codeStyle: { - lineWidth: 80, - cjsToEsm: true, - importSymbol: "import", - indentSize: 2, - indentStyle: "space", - dontRemoveComments: false, - shouldAddComments: true, - typeOrInterface: "type", - importOrRequire: "import", - quoteMark: "double", - semicolons: true, - trailingComma: "all", - bracketSpacing: true, - arrowParens: "always", - tabWidth: 2, - jsToTs: false, - modernize: { - replaceFs: true, - replacePath: true, - replaceHttp: true, - replaceProcess: true, - replaceConsole: true, - replaceEvents: true, - ...rules.experimental?.codeStyle?.modernize, - }, - ...rules.experimental?.codeStyle, + codeStyle: { + lineWidth: 80, + cjsToEsm: true, + importSymbol: "import", + indentSize: 2, + indentStyle: "space", + dontRemoveComments: false, + shouldAddComments: true, + typeOrInterface: "type", + importOrRequire: "import", + quoteMark: "double", + semicolons: true, + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + tabWidth: 2, + jsToTs: false, + modernize: { + replaceFs: true, + replacePath: true, + replaceHttp: true, + replaceProcess: true, + replaceConsole: true, + replaceEvents: true, + ...rules.codeStyle?.modernize, }, + ...rules.codeStyle, + }, - // Project features - features: { - i18n: false, - analytics: false, - themeMode: "dark-light", - authentication: false, - api: false, - database: false, - testing: false, - docker: false, - ci: false, - commands: [], - webview: [], - language: [], - themes: [], - ...rules.experimental?.features, - }, + // Project features + features: { + i18n: false, + analytics: false, + themeMode: "dark-light", + authentication: false, + api: false, + database: false, + testing: false, + docker: false, + ci: false, + commands: [], + webview: [], + language: [], + themes: [], + ...rules.features, + }, - // Dependencies management - ignoreDependencies: rules.experimental?.ignoreDependencies ?? [], + // Dependencies management + ignoreDependencies: rules.ignoreDependencies ?? [], - // Custom rules - customRules: rules.experimental?.customRules ?? {}, + // Custom rules + customRules: rules.customRules ?? {}, - // Generation preferences - skipPromptsUseAutoBehavior: - rules.experimental?.skipPromptsUseAutoBehavior ?? false, - deployBehavior: rules.experimental?.deployBehavior ?? "prompt", - depsBehavior: rules.experimental?.depsBehavior ?? "prompt", - gitBehavior: rules.experimental?.gitBehavior ?? "prompt", - i18nBehavior: rules.experimental?.i18nBehavior ?? "prompt", - scriptsBehavior: rules.experimental?.scriptsBehavior ?? "prompt", - }, + // Generation preferences + skipPromptsUseAutoBehavior: rules.skipPromptsUseAutoBehavior ?? false, + deployBehavior: rules.deployBehavior ?? "prompt", + depsBehavior: rules.depsBehavior ?? "prompt", + gitBehavior: rules.gitBehavior ?? "prompt", + i18nBehavior: rules.i18nBehavior ?? "prompt", + scriptsBehavior: rules.scriptsBehavior ?? "prompt", }; try { diff --git a/src/args/help/mod.ts b/src/args/help/mod.ts index a1883c4b..419d0c53 100644 --- a/src/args/help/mod.ts +++ b/src/args/help/mod.ts @@ -1,6 +1,6 @@ import { defineCommand } from "@reliverse/prompts"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export default defineCommand({ meta: { diff --git a/src/args/login/impl.ts b/src/args/login/impl.ts index b9038c64..61892e5f 100644 --- a/src/args/login/impl.ts +++ b/src/args/login/impl.ts @@ -15,10 +15,13 @@ import url from "url"; import type { ReliverseMemory } from "~/types.js"; -import { getReliverseMemory, updateReliverseMemory } from "~/app/app-utils.js"; import { MEMORY_FILE } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { showAnykeyPrompt } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/showAnykeyPrompt.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { + getReliverseMemory, + updateReliverseMemory, +} from "~/utils/reliverseMemory.js"; /** * Custom error for when a user cancels the process. diff --git a/src/args/login/mod.ts b/src/args/login/mod.ts index 402d5b80..75890f05 100644 --- a/src/args/login/mod.ts +++ b/src/args/login/mod.ts @@ -1,9 +1,9 @@ import { defineCommand } from "@reliverse/prompts"; -import { getReliverseMemory } from "~/app/app-utils.js"; import { useLocalhost } from "~/app/constants.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { showAnykeyPrompt } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/showAnykeyPrompt.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { getReliverseMemory } from "~/utils/reliverseMemory.js"; import { auth } from "./impl.js"; diff --git a/src/args/logout/impl.ts b/src/args/logout/impl.ts index 26c47add..efbc3bbb 100644 --- a/src/args/logout/impl.ts +++ b/src/args/logout/impl.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import os from "os"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function deleteMemory() { const homeDir = os.homedir(); diff --git a/src/args/logout/mod.ts b/src/args/logout/mod.ts index 4d44cb8c..50c4b2a2 100644 --- a/src/args/logout/mod.ts +++ b/src/args/logout/mod.ts @@ -8,7 +8,7 @@ import fs from "fs-extra"; import os from "os"; import path from "pathe"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; import { deleteMemory } from "./impl.js"; diff --git a/src/args/memory/mod.ts b/src/args/memory/mod.ts index 4d0deceb..a1b07bee 100644 --- a/src/args/memory/mod.ts +++ b/src/args/memory/mod.ts @@ -1,7 +1,7 @@ import { defineCommand } from "@reliverse/prompts"; -import { getReliverseMemory } from "~/app/app-utils.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; +import { getReliverseMemory } from "~/utils/reliverseMemory.js"; export default defineCommand({ meta: { diff --git a/src/args/studio/mod.ts b/src/args/studio/mod.ts index aa3f96b9..c93e54d1 100644 --- a/src/args/studio/mod.ts +++ b/src/args/studio/mod.ts @@ -1,6 +1,6 @@ import { defineCommand } from "@reliverse/prompts"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export default defineCommand({ meta: { diff --git a/src/dev.ts b/src/dev.ts index 6d6f66bd..b170059f 100644 --- a/src/dev.ts +++ b/src/dev.ts @@ -2,14 +2,9 @@ import { confirmPrompt, selectPrompt } from "@reliverse/prompts"; import fs from "fs-extra"; import path from "pathe"; -import type { - ReliverseConfig, - ReliverseMemory, - TemplateOption, -} from "~/types.js"; +import type { ReliverseMemory, TemplateOption } from "~/types.js"; import { downloadTemplate } from "~/app/menu/create-project/cp-modules/cli-main-modules/downloads/downloadTemplate.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; import { cd, pwd, @@ -18,6 +13,9 @@ import { import { askProjectName } from "~/app/menu/create-project/cp-modules/cli-main-modules/modules/askProjectName.js"; import { composeEnvFile } from "~/app/menu/create-project/cp-modules/compose-env-file/mod.js"; import { promptGitDeploy } from "~/app/menu/create-project/cp-modules/git-deploy-prompts/mod.js"; +import { relinka } from "~/utils/loggerRelinka.js"; + +import type { ReliverseConfig } from "./utils/reliverseConfig.js"; import { FALLBACK_ENV_EXAMPLE_URL } from "./app/constants.js"; @@ -41,6 +39,10 @@ export async function showDevToolsMenu( "downloadTemplate + cd tests-runtime + composeEnvFile + promptGitDeploy", value: "download-template", }, + { + label: "Create config or force re-population", + value: "force-populate-config", + }, { label: "Exit", value: "exit" }, ], }); diff --git a/src/main.ts b/src/main.ts index aecc3709..effe4c5a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,12 +2,12 @@ import { defineCommand, errorHandler, runMain } from "@reliverse/prompts"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; const main = defineCommand({ meta: { name: "reliverse", - version: "1.4.19", + version: "1.4.20", description: "https://docs.reliverse.org", }, subCommands: { diff --git a/src/types.ts b/src/types.ts index 2fd65f04..fa3ba754 100644 --- a/src/types.ts +++ b/src/types.ts @@ -118,20 +118,20 @@ export type PreferredLibraries = { }; export type CodeStylePreferences = { - dontRemoveComments: boolean; - shouldAddComments: boolean; - typeOrInterface: "type" | "interface" | "mixed"; - importOrRequire: "import" | "require" | "mixed"; - quoteMark: "single" | "double"; - semicolons: boolean; - lineWidth: number; - indentStyle: "space" | "tab"; - indentSize: 2 | 4 | 8; + dontRemoveComments?: boolean; + shouldAddComments?: boolean; + typeOrInterface?: "type" | "interface" | "mixed"; + importOrRequire?: "import" | "require" | "mixed"; + quoteMark?: "single" | "double"; + semicolons?: boolean; + lineWidth?: number; + indentStyle?: "space" | "tab"; + indentSize?: 2 | 4 | 8; cjsToEsm?: boolean; - trailingComma: "all" | "es5" | "none"; - bracketSpacing: boolean; - arrowParens: "always" | "as-needed" | "never"; - tabWidth: 2 | 4 | 8; + trailingComma?: "all" | "es5" | "none"; + bracketSpacing?: boolean; + arrowParens?: "always" | "as-needed" | "never"; + tabWidth?: 2 | 4 | 8; modernize?: { replaceFs?: boolean; replacePath?: boolean; @@ -184,154 +184,7 @@ export type ProjectSubcategory = "" | "e-commerce" | "tool"; export type ProjectState = "" | "creating" | "created"; -export type ReliverseConfig = { - experimental?: - | { - // Do you want autoYes/autoNo below? - // Set to true to activate auto-answering. - // This is to ensure there is no unexpected behavior. - skipPromptsUseAutoBehavior?: boolean | undefined; - - // Generation preferences - deployBehavior?: Behavior | undefined; - depsBehavior?: Behavior | undefined; - gitBehavior?: Behavior | undefined; - i18nBehavior?: Behavior | undefined; - scriptsBehavior?: Behavior | undefined; - - // Project details - projectName?: string | undefined; - projectAuthor?: string | undefined; - projectDescription?: string | undefined; - projectVersion?: string | undefined; - projectLicense?: string | undefined; - projectRepository?: string | undefined; - projectState?: ProjectState | undefined; - projectDomain?: string | undefined; - projectType?: ProjectTypeOptions | undefined; - projectCategory?: ProjectCategory | undefined; - projectSubcategory?: ProjectSubcategory | undefined; - projectTemplate?: TemplateOption | undefined; - projectDeployService?: DeploymentService | undefined; - projectActivation?: string | undefined; - projectFramework?: string | undefined; - projectPackageManager?: string | undefined; - projectFrameworkVersion?: string | undefined; - projectDisplayName?: string | undefined; - nodeVersion?: string | undefined; - runtime?: string | undefined; - productionBranch?: string | undefined; - deployUrl?: string | undefined; - monorepo?: - | { - type: string; - packages: string[]; - sharedPackages: string[]; - } - | undefined; - - // Project features - features?: - | { - i18n?: boolean | undefined; - analytics?: boolean | undefined; - themeMode?: "dark-light" | "dark" | "light" | undefined; - authentication?: boolean | undefined; - api?: boolean | undefined; - database?: boolean | undefined; - testing?: boolean | undefined; - docker?: boolean | undefined; - ci?: boolean | undefined; - commands?: string[] | undefined; - webview?: string[] | undefined; - language?: string[] | undefined; - themes?: string[] | undefined; - } - | undefined; - - // Development preferences - preferredLibraries?: - | { - stateManagement?: string | undefined; - formManagement?: string | undefined; - styling?: string | undefined; - uiComponents?: string | undefined; - testing?: string | undefined; - authentication?: string | undefined; - database?: string | undefined; - api?: string | undefined; - linting?: string | undefined; - formatting?: string | undefined; - deployment?: string | undefined; - payment?: string | undefined; - analytics?: string | undefined; - monitoring?: string | undefined; - logging?: string | undefined; - forms?: string | undefined; - validation?: string | undefined; - documentation?: string | undefined; - components?: string | undefined; - icons?: string | undefined; - mail?: string | undefined; - search?: string | undefined; - cache?: string | undefined; - storage?: string | undefined; - cdn?: string | undefined; - cms?: string | undefined; - i18n?: string | undefined; - seo?: string | undefined; - ui?: string | undefined; - motion?: string | undefined; - charts?: string | undefined; - dates?: string | undefined; - markdown?: string | undefined; - security?: string | undefined; - notifications?: string | undefined; - uploads?: string | undefined; - routing?: string | undefined; - } - | undefined; - - // Code style preferences - codeStyle?: - | { - lineWidth?: number | undefined; - cjsToEsm?: boolean | undefined; - importSymbol?: string | undefined; - indentSize?: number | undefined; - indentStyle?: "space" | "tab" | undefined; - dontRemoveComments?: boolean | undefined; - shouldAddComments?: boolean | undefined; - typeOrInterface?: "type" | "interface" | undefined; - importOrRequire?: "import" | "require" | undefined; - quoteMark?: "single" | "double" | undefined; - semicolons?: boolean | undefined; - trailingComma?: "all" | "es5" | "none" | undefined; - bracketSpacing?: boolean | undefined; - arrowParens?: "always" | "avoid" | undefined; - tabWidth?: number | undefined; - jsToTs?: boolean | undefined; - modernize?: - | { - replaceFs?: boolean | undefined; - replacePath?: boolean | undefined; - replaceHttp?: boolean | undefined; - replaceProcess?: boolean | undefined; - replaceConsole?: boolean | undefined; - replaceEvents?: boolean | undefined; - } - | undefined; - } - | undefined; - - // Dependencies management - ignoreDependencies?: string[] | undefined; - - // Custom rules - customRules?: Record | undefined; - } - | undefined; -}; +export type PackageManagerName = "npm" | "yarn" | "pnpm" | "bun" | "deno"; // Return type explicitly first export type BiomeConfigResult = { @@ -406,12 +259,6 @@ export type NextJsConfig = BaseConfig & { hostname: string; }[]; }; - experimental?: { - typedRoutes?: boolean; - serverActions?: { - allowedOrigins?: string[]; - }; - }; logging?: { fetches?: { fullUrl?: boolean; @@ -662,18 +509,6 @@ export type ModernizeConfig = { replaceEvents?: boolean; }; -export type DetectedProject = { - name: string; - path: string; - config: ReliverseConfig; - gitStatus?: { - uncommittedChanges: number; - unpushedCommits: number; - }; - needsDepsInstall?: boolean; - hasGit?: boolean; -}; - export type GitCommitOptions = { message: string; projectPath: string; diff --git a/src/utils/configHandler.ts b/src/utils/configHandler.ts new file mode 100644 index 00000000..074d6ffd --- /dev/null +++ b/src/utils/configHandler.ts @@ -0,0 +1,113 @@ +import { parseJSONC } from "confbox"; +import fs from "fs-extra"; +import path from "pathe"; + +import type { + BaseConfig, + BiomeConfig, + BiomeConfigResult, + ConfigFile, +} from "~/types.js"; + +import { relinka } from "~/utils/loggerRelinka.js"; + +export const CONFIG_FILES: ConfigFile[] = [ + { + name: "TypeScript", + files: ["tsconfig.json"], + editPrompt: "Edit TypeScript configuration", + }, + { + name: "Biome", + files: ["biome.json", "biome.jsonc"], + editPrompt: "Edit Biome configuration", + }, + { + name: "Knip", + files: ["knip.json", "knip.jsonc"], + editPrompt: "Edit Knip configuration", + }, + { + name: "ESLint", + files: ["eslint.config.js"], + editPrompt: "Edit ESLint configuration", + }, + { + name: "Vitest", + files: ["vitest.config.ts"], + editPrompt: "Edit Vitest configuration", + }, + { + name: "Prettier", + files: [ + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yml", + ".prettierrc.yaml", + ".prettierrc.json5", + ".prettierrc.js", + "prettier.config.js", + ], + editPrompt: "Edit Prettier configuration", + }, +]; + +export async function detectConfigFiles(cwd: string): Promise { + const detectedConfigs: ConfigFile[] = []; + + for (const config of CONFIG_FILES) { + for (const file of config.files) { + if (await fs.pathExists(path.join(cwd, file))) { + detectedConfigs.push(config); + break; + } + } + } + + return detectedConfigs; +} + +// Helper function to add metadata to configs +export function addConfigMetadata(config: T): T & BaseConfig { + return { + ...config, + version: "0.1.0", + generatedAt: new Date().toISOString(), + }; +} + +let cachedBiomeConfig: BiomeConfigResult = null; + +export async function getBiomeConfig( + projectPath: string, +): Promise { + if (cachedBiomeConfig !== null) { + return cachedBiomeConfig; + } + + try { + const biomePath = path.join(projectPath, "biome.jsonc"); + if (await fs.pathExists(biomePath)) { + const content = await fs.readFile(biomePath, "utf-8"); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const config = parseJSONC(content) as BiomeConfig; + cachedBiomeConfig = { + lineWidth: config.formatter?.lineWidth ?? 80, + indentStyle: config.formatter?.indentStyle ?? "space", + indentWidth: config.formatter?.indentWidth ?? 2, + quoteMark: config.javascript?.formatter?.quoteStyle ?? "double", + semicolons: config.javascript?.formatter?.semicolons === "always", + trailingComma: config.javascript?.formatter?.trailingComma === "all", + }; + return cachedBiomeConfig; + } + } catch (error) { + relinka( + "error-verbose", + "Error reading biome config:", + error instanceof Error ? error.message : String(error), + ); + } + cachedBiomeConfig = null; + return null; +} diff --git a/src/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.ts b/src/utils/loggerRelinka.ts similarity index 100% rename from src/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.ts rename to src/utils/loggerRelinka.ts diff --git a/src/utils/reliverseConfig.ts b/src/utils/reliverseConfig.ts new file mode 100644 index 00000000..7f97b55f --- /dev/null +++ b/src/utils/reliverseConfig.ts @@ -0,0 +1,1468 @@ +import { parseJSONC } from "confbox"; +import destr from "destr"; +import { safeDestr } from "destr"; +import { detect } from "detect-package-manager"; +import fs from "fs-extra"; +import path from "pathe"; +import { readPackageJSON, type PackageJson, type TSConfig } from "pkg-types"; +import { z } from "zod"; + +import type { + Behavior, + CodeStylePreferences, + DeploymentService, + ProjectSubcategory, + PreferredLibraries, + ProjectCategory, + ProjectState, + ProjectTypeOptions, + TemplateOption, + PackageManagerName, +} from "~/types.js"; + +import { getBiomeConfig } from "~/utils/configHandler.js"; + +import { relinka } from "./loggerRelinka.js"; + +type GenerateReliverseConfigOptions = { + projectName: string; + frontendUsername: string; + deployService: DeploymentService; + primaryDomain: string; + projectPath: string; + i18nShouldBeEnabled: boolean; + overwrite?: boolean; + githubUsername: string; +}; + +type ProjectFeatures = { + i18n: boolean; + analytics: boolean; + themeMode: "light" | "dark" | "dark-light"; + authentication: boolean; + api: boolean; + database: boolean; + testing: boolean; + docker: boolean; + ci: boolean; + commands: string[]; + webview: string[]; + language: string[]; + themes: string[]; +}; + +export type ReliverseConfig = { + // Do you want autoYes/autoNo below? + // Set to true to activate auto-answering. + // This is to ensure there is no unexpected behavior. + skipPromptsUseAutoBehavior?: boolean | undefined; + + // Generation preferences + deployBehavior?: Behavior | undefined; + depsBehavior?: Behavior | undefined; + gitBehavior?: Behavior | undefined; + i18nBehavior?: Behavior | undefined; + scriptsBehavior?: Behavior | undefined; + + // Project details + projectName?: string | undefined; + projectAuthor?: string | undefined; + projectDescription?: string | undefined; + projectVersion?: string | undefined; + projectLicense?: string | undefined; + projectRepository?: string | undefined; + projectState?: ProjectState | undefined; + projectDomain?: string | undefined; + projectType?: ProjectTypeOptions | undefined; + projectCategory?: ProjectCategory | undefined; + projectSubcategory?: ProjectSubcategory | undefined; + projectTemplate?: TemplateOption | undefined; + projectDeployService?: DeploymentService | undefined; + projectActivation?: string | undefined; + projectFramework?: string | undefined; + projectPackageManager?: PackageManagerName | undefined; + projectFrameworkVersion?: string | undefined; + projectDisplayName?: string | undefined; + nodeVersion?: string | undefined; + runtime?: string | undefined; + productionBranch?: string | undefined; + deployUrl?: string | undefined; + monorepo?: + | { + type: string; + packages: string[]; + sharedPackages: string[]; + } + | undefined; + + // Project features + features?: + | { + i18n?: boolean | undefined; + analytics?: boolean | undefined; + themeMode?: "dark-light" | "dark" | "light" | undefined; + authentication?: boolean | undefined; + api?: boolean | undefined; + database?: boolean | undefined; + testing?: boolean | undefined; + docker?: boolean | undefined; + ci?: boolean | undefined; + commands?: string[] | undefined; + webview?: string[] | undefined; + language?: string[] | undefined; + themes?: string[] | undefined; + } + | undefined; + + // Development preferences + preferredLibraries?: PreferredLibraries | undefined; + codeStyle?: CodeStylePreferences | undefined; + + // Dependencies management + ignoreDependencies?: string[] | undefined; + + // Custom rules + customRules?: Record | undefined; +}; + +export type DetectedProject = { + name: string; + path: string; + config: ReliverseConfig; + gitStatus?: { + uncommittedChanges: number; + unpushedCommits: number; + }; + needsDepsInstall?: boolean; + hasGit?: boolean; +}; + +const BACKUP_EXTENSION = ".backup"; +const TEMP_EXTENSION = ".tmp"; + +export const DEFAULT_CONFIG: ReliverseConfig = { + // Project details + projectAuthor: "", + projectState: "", + projectDomain: "", + projectType: "", + projectCategory: "", + projectSubcategory: "", + + // Development preferences + projectFramework: "nextjs", + projectPackageManager: "npm", + preferredLibraries: { + stateManagement: "zustand", + formManagement: "react-hook-form", + styling: "tailwind", + uiComponents: "shadcn-ui", + testing: "bun", + authentication: "clerk", + database: "drizzle", + api: "trpc", + }, + + // Project features + features: { + i18n: true, + analytics: false, + themeMode: "dark-light", + authentication: true, + api: true, + database: true, + testing: false, + docker: false, + ci: false, + commands: [], + webview: [], + language: [], + themes: [], + }, + + // Code style preferences + codeStyle: { + dontRemoveComments: true, + shouldAddComments: true, + typeOrInterface: "type", + importOrRequire: "import", + quoteMark: "double", + semicolons: true, + lineWidth: 80, + indentStyle: "space", + indentSize: 2, + importSymbol: "~", + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + tabWidth: 2, + jsToTs: false, + cjsToEsm: false, + modernize: { + replaceFs: false, + replacePath: false, + replaceHttp: false, + replaceProcess: false, + replaceConsole: false, + replaceEvents: false, + }, + }, + + // Generation preferences + skipPromptsUseAutoBehavior: false, + deployBehavior: "prompt", + depsBehavior: "prompt", + gitBehavior: "prompt", + i18nBehavior: "prompt", + scriptsBehavior: "prompt", +}; + +// Feature schema +const featuresSchema = z.object({ + i18n: z.boolean().default(true), + analytics: z.boolean().default(false), + themeMode: z.enum(["light", "dark", "dark-light"]).default("dark-light"), + authentication: z.boolean().default(true), + api: z.boolean().default(true), + database: z.boolean().default(true), + testing: z.boolean().default(false), + docker: z.boolean().default(false), + ci: z.boolean().default(false), + commands: z.array(z.string()).default([]), + webview: z.array(z.string()).default([]), + language: z.array(z.string()).default(["typescript"]), + themes: z.array(z.string()).default(["default"]), +}); + +// Code style schema +const codeStyleSchema = z + .object({ + lineWidth: z.number().min(1).max(200), + indentSize: z.union([z.literal(2), z.literal(4), z.literal(8)]), + indentStyle: z.enum(["space", "tab"]), + quoteMark: z.enum(["single", "double"]), + semicolons: z.boolean(), + trailingComma: z.enum(["none", "es5", "all"]), + bracketSpacing: z.boolean(), + arrowParens: z.enum(["always", "as-needed", "never"]), + tabWidth: z.union([z.literal(2), z.literal(4), z.literal(8)]), + jsToTs: z.boolean(), + dontRemoveComments: z.boolean(), + shouldAddComments: z.boolean(), + typeOrInterface: z.enum(["type", "interface"]), + importOrRequire: z.enum(["import", "require", "mixed"]), + cjsToEsm: z.boolean(), + modernize: z.object({ + replaceFs: z.boolean(), + replacePath: z.boolean(), + replaceHttp: z.boolean(), + replaceProcess: z.boolean(), + replaceConsole: z.boolean(), + replaceEvents: z.boolean(), + }), + importSymbol: z.string(), + }) + .default({ + lineWidth: 80, + indentSize: 2, + indentStyle: "space", + quoteMark: "double", + semicolons: true, + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + tabWidth: 2, + jsToTs: false, + dontRemoveComments: true, + shouldAddComments: true, + typeOrInterface: "type", + importOrRequire: "import", + cjsToEsm: false, + modernize: { + replaceFs: false, + replacePath: false, + replaceHttp: false, + replaceProcess: false, + replaceConsole: false, + replaceEvents: false, + }, + importSymbol: "", + }); + +const monorepoSchema = z + .object({ + type: z.enum(["none", "turborepo", "nx", "pnpm"]), + packages: z.array(z.string()), + sharedPackages: z.array(z.string()), + }) + .default({ + type: "none", + packages: [], + sharedPackages: [], + }); + +export const PROJECT_TYPE_FILES = { + "": [], + library: ["jsr.json", "jsr.jsonc"], + nextjs: ["next.config.js", "next.config.ts", "next.config.mjs"], + astro: ["astro.config.js", "astro.config.ts", "astro.config.mjs"], + react: ["vite.config.js", "vite.config.ts", "react.config.js"], + vue: ["vue.config.js", "vite.config.ts"], + svelte: ["svelte.config.js", "svelte.config.ts"], +} satisfies Record; + +export async function detectProjectType( + cwd: string, +): Promise { + for (const [type, files] of Object.entries(PROJECT_TYPE_FILES)) { + for (const file of files) { + if (await fs.pathExists(path.join(cwd, file))) { + return type as keyof typeof PROJECT_TYPE_FILES; + } + } + } + return null; +} + +export async function generateDefaultRulesForProject( + cwd: string, +): Promise { + const projectType = await detectProjectType(cwd); + if (!projectType) { + return null; + } + + const packageJsonPath = path.join(cwd, "package.json"); + let packageJson: any = {}; + try { + if (await fs.pathExists(packageJsonPath)) { + packageJson = safeDestr(await fs.readFile(packageJsonPath, "utf-8")); + } + } catch (error) { + relinka( + "error", + "Error reading package.json:", + error instanceof Error ? error.message : String(error), + ); + } + + const rules = await getDefaultReliverseConfig( + cwd, + (packageJson.name as string) ?? path.basename(cwd), + (packageJson.author as string) ?? "user", + projectType, + ); + + // Detect additional features + const hasI18n = await fs.pathExists(path.join(cwd, "src/app/[locale]")); + const hasPrisma = await fs.pathExists(path.join(cwd, "prisma/schema.prisma")); + const hasDrizzle = await fs.pathExists(path.join(cwd, "drizzle.config.ts")); + const hasNextAuth = await fs.pathExists( + path.join(cwd, "src/app/api/auth/[...nextauth]"), + ); + const hasClerk = packageJson.dependencies?.["@clerk/nextjs"]; + + // Configure features + rules.features = { + ...rules.features, + i18n: hasI18n, + database: hasPrisma || hasDrizzle, + authentication: hasNextAuth || hasClerk, + analytics: false, + themeMode: "dark-light", + api: true, + testing: false, + docker: false, + ci: false, + commands: [], + webview: [], + language: ["typescript"], + themes: ["default"], + }; + + // Configure preferred libraries + if (!rules.preferredLibraries) { + rules.preferredLibraries = {}; + } + + // Set database preference based on project type + if (projectType === "nextjs") { + rules.preferredLibraries.database = "prisma"; + } else { + rules.preferredLibraries.database = "drizzle"; + } + + // Set auth preference based on project type + if (projectType === "nextjs") { + rules.preferredLibraries.authentication = "next-auth"; + } else { + rules.preferredLibraries.authentication = "clerk"; + } + + return rules; +} + +export async function getDefaultReliverseConfig( + cwd: string, + projectName: string, + projectAuthor: string, + projectFramework = "nextjs", +): Promise { + const biomeConfig = await getBiomeConfig(cwd); + const detectedPkgManager = await detect(); + + // Read package.json + let packageData: PackageJson = { name: projectName, author: projectAuthor }; + + try { + packageData = await readPackageJSON(); + } catch { + // Use default values if package.json doesn't exist + } + + return { + // Project details + projectName: packageData.name ?? projectName, + projectAuthor: + typeof packageData.author === "object" + ? (packageData.author.name ?? projectAuthor) + : (packageData.author ?? projectAuthor), + projectDescription: packageData.description ?? "", + projectVersion: packageData.version ?? "0.1.0", + projectLicense: packageData.license ?? "MIT", + projectRepository: + (typeof packageData.repository === "string" + ? packageData.repository + : packageData.repository?.url) ?? "", + + // Project features + features: { + i18n: false, + analytics: false, + themeMode: "dark-light", + authentication: false, + api: false, + database: false, + testing: false, + docker: false, + ci: false, + commands: [], + webview: [], + language: [], + themes: [], + }, + + // Development preferences + projectFramework, + projectPackageManager: detectedPkgManager, + projectFrameworkVersion: undefined, + nodeVersion: undefined, + runtime: undefined, + monorepo: { + type: "none", + packages: [], + sharedPackages: [], + }, + preferredLibraries: {}, + codeStyle: { + lineWidth: biomeConfig?.lineWidth ?? 80, + indentSize: biomeConfig?.indentWidth ?? 2, + indentStyle: "space", + quoteMark: "double", + semicolons: true, + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + tabWidth: 2, + jsToTs: false, + }, + + // Dependencies management + ignoreDependencies: [], + + // Custom rules + customRules: {}, + + // Generation preferences + skipPromptsUseAutoBehavior: false, + deployBehavior: "prompt", + depsBehavior: "prompt", + gitBehavior: "prompt", + i18nBehavior: "prompt", + scriptsBehavior: "prompt", + }; +} + +export const reliverseConfigSchema: z.ZodType = z.object({ + // Project details + projectName: z.string().min(1), + projectAuthor: z.string().min(1), + projectDescription: z.string().default(""), + projectVersion: z + .string() + .regex(/^\d+\.\d+\.\d+/) + .default("0.1.0"), + projectLicense: z.string().default("MIT"), + projectRepository: z.string().url().optional(), + projectDeployService: z.enum(["vercel", "netlify", "railway"]).optional(), + projectDomain: z.string().url().optional(), + + // Project features + features: featuresSchema.default({}), + + // Development preferences + projectFramework: z.string().default("nextjs"), + projectPackageManager: z.enum(["npm", "pnpm", "yarn", "bun"]).default("bun"), + projectFrameworkVersion: z.string().optional(), + nodeVersion: z.string().optional(), + runtime: z.string().optional(), + monorepo: monorepoSchema, + preferredLibraries: z.record(z.string()).default({}), + codeStyle: codeStyleSchema, + + // Dependencies management + ignoreDependencies: z.array(z.string()).default([]), + + // Custom rules + customRules: z.record(z.unknown()).default({}), + + // Generation preferences + skipPromptsUseAutoBehavior: z.boolean().default(false), + deployBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), + depsBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), + gitBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), + i18nBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), + scriptsBehavior: z.enum(["prompt", "autoYes", "autoNo"]).default("prompt"), +}); + +// Types for comment sections +type CommentSections = Partial>; + +/** + * Safely writes config to file with backup and atomic operations + */ +export async function writeReliverseConfig( + configPath: string, + config: ReliverseConfig, +): Promise { + const backupPath = configPath + BACKUP_EXTENSION; + const tempPath = configPath + TEMP_EXTENSION; + + try { + // Validate config before writing + const validationResult = reliverseConfigSchema.safeParse(config); + if (!validationResult.success) { + throw new Error( + `Invalid config: ${validationResult.error.errors + .map((e) => `${e.path.join(".")}: ${e.message}`) + .join(", ")}`, + ); + } + + // Helper function to create comment + const c = (text: string) => `// ${text}`; + + // Define comment sections with only essential comments + const commentSections: CommentSections = { + skipPromptsUseAutoBehavior: [ + c("Do you want autoYes/autoNo below?"), + c("Set to true to activate auto-answering."), + c("This is to ensure there is no unexpected behavior."), + ], + features: [c("Project capabilities")], + projectFramework: [c("Tech stack of your project")], + codeStyle: [c("Code style preferences")], + ignoreDependencies: [c("Cleaner codemod will ignore these deps")], + customRules: [c("Custom rules for Reliverse AI")], + deployBehavior: [c("Prompts behavior (prompt | autoYes | autoNo)")], + }; + + // Format with 2 spaces indentation + let content = JSON.stringify(validationResult.data, null, 2); + + // Add section comments + Object.entries(commentSections).forEach(([section, comments]) => { + // Add section title with proper spacing + content = content.replace( + `"${section}":`, + `${comments}\n "${section}":`, + ); + + const formattedComments = (Array.isArray(comments) ? comments : []) + .map( + (comment: string, index: number, array: string[]) => + index === array.length - 1 + ? ` ${comment}` // Last comment + : ` ${comment}\n`, // Other comments + ) + .join(""); + content = content.replace( + new RegExp(`(\\s+)"${section}":`, "g"), + `\n\n${formattedComments}\n "${section}":`, + ); + }); + + // Clean up multiple empty lines and ensure final newline + content = `${content + .replace(/\n{3,}/g, "\n\n") // Replace 3 or more newlines with 2 + .replace(/{\n\n/g, "{\n") // Remove double newline after opening brace + .replace(/\n\n}/g, "\n}") // Remove double newline before closing brace + .trim()}\n`; // Ensure single newline at end + + // Create backup if file exists + if (await fs.pathExists(configPath)) { + await fs.copy(configPath, backupPath); + } + + // Write to temp file first + await fs.writeFile(tempPath, content); + + // Atomically rename temp file to actual file + await fs.rename(tempPath, configPath); + + // Remove backup on success + if (await fs.pathExists(backupPath)) { + await fs.remove(backupPath); + } + + relinka("success-verbose", "Config written successfully"); + } catch (error) { + // Restore from backup if write failed + if ( + (await fs.pathExists(backupPath)) && + !(await fs.pathExists(configPath)) + ) { + await fs.copy(backupPath, configPath); + relinka("warn", "Restored config from backup after failed write"); + } + + // Clean up temp file + if (await fs.pathExists(tempPath)) { + await fs.remove(tempPath); + } + + throw error; + } +} + +/** + * Safely reads and validates config from file + */ +export async function readReliverseConfig( + projectName: string, + configPath: string, + cwd: string, +): Promise { + const backupPath = configPath + BACKUP_EXTENSION; + + try { + // Try reading main file + if (await fs.pathExists(configPath)) { + const content = await fs.readFile(configPath, "utf-8"); + + // Handle empty file or just {} + if (!content.trim() || content.trim() === "{}") { + const defaultConfig = await getDefaultReliverseConfig( + cwd, + projectName, + "user", + ); + await writeReliverseConfig(configPath, defaultConfig); + return defaultConfig; + } + + const parsed = destr(content); + if (!parsed || typeof parsed !== "object") { + const defaultConfig = await getDefaultReliverseConfig( + cwd, + path.basename(configPath), + "user", + ); + await writeReliverseConfig(configPath, defaultConfig); + return defaultConfig; + } + + // Validate parsed content + const validationResult = reliverseConfigSchema.safeParse(parsed); + if (validationResult.success) { + // If config is valid, return it as is without any merging + return validationResult.data; + } + + // Only merge if there are missing required fields + const missingFields = validationResult.error.errors.some( + (e) => e.code === "invalid_type" && e.received === "undefined", + ); + + if (!missingFields) { + // If errors are not about missing fields, return null to trigger backup/default + relinka( + "warn", + `Invalid config format: ${validationResult.error.errors + .map((e) => `${e.path.join(".")}: ${e.message}`) + .join(", ")}`, + ); + return null; + } + + // If we have missing fields, merge with defaults + const defaultConfig = await getDefaultReliverseConfig( + cwd, + path.basename(configPath), + "user", + ); + + // Deep merge existing values with defaults + const mergedConfig: ReliverseConfig = { + ...defaultConfig, + ...(parsed as Partial), + features: { + ...defaultConfig?.features, + ...(parsed as Partial)?.features, + }, + codeStyle: { + ...defaultConfig?.codeStyle, + ...(parsed as Partial)?.codeStyle, + }, + preferredLibraries: { + ...defaultConfig?.preferredLibraries, + ...(parsed as Partial)?.preferredLibraries, + }, + customRules: { + ...defaultConfig?.customRules, + ...(parsed as Partial)?.customRules, + }, + }; + + // Validate merged config + const mergedValidation = reliverseConfigSchema.safeParse(mergedConfig); + if (mergedValidation.success) { + await writeReliverseConfig(configPath, mergedValidation.data); + relinka( + "info", + "Updated config with missing fields while preserving existing values", + ); + return mergedValidation.data; + } + + // If merged config is invalid, warn and try backup + relinka( + "warn", + `Invalid config format: ${validationResult.error.errors + .map((e) => `${e.path.join(".")}: ${e.message}`) + .join(", ")}`, + ); + } + + // Try reading backup if main file is invalid or missing + if (await fs.pathExists(backupPath)) { + const backupContent = await fs.readFile(backupPath, "utf-8"); + const parsed = destr(backupContent); + + const validationResult = reliverseConfigSchema.safeParse(parsed); + if (validationResult.success) { + // Restore from backup + await fs.copy(backupPath, configPath); + relinka("info", "Restored config from backup"); + return validationResult.data; + } + } + + // If no valid config found, create default + const defaultConfig = await getDefaultReliverseConfig( + cwd, + projectName, + "user", + ); + await writeReliverseConfig(configPath, defaultConfig); + return defaultConfig; + } catch (error) { + relinka( + "error", + "Error reading config:", + error instanceof Error ? error.message : String(error), + ); + return null; + } +} + +/** + * Safely updates specific fields in the config + */ +export async function updateReliverseConfig( + projectName: string, + configPath: string, + updates: Partial, + cwd: string, +): Promise { + try { + const currentConfig = await readReliverseConfig( + projectName, + configPath, + cwd, + ); + if (!currentConfig) { + return false; + } + + const updatedConfig = { + ...currentConfig, + ...updates, + }; + + await writeReliverseConfig(configPath, updatedConfig); + return true; + } catch (error) { + relinka( + "error", + "Error updating config:", + error instanceof Error ? error.message : String(error), + ); + return false; + } +} + +const DEFAULT_MONOREPO = { + type: "none" as const, + packages: [], + sharedPackages: [], +}; + +async function createReliverseConfig( + cwd: string, + githubUsername: string, +): Promise { + const defaultRules = await generateDefaultRulesForProject(cwd); + + await generateReliverseConfig({ + projectName: defaultRules?.projectName ?? path.basename(cwd), + frontendUsername: defaultRules?.projectAuthor ?? "user", + deployService: "vercel", + primaryDomain: defaultRules?.projectDomain ?? "", + projectPath: cwd, + i18nShouldBeEnabled: defaultRules?.features?.i18n ?? false, + githubUsername, + }); + + relinka( + "info", + defaultRules + ? "Created .reliverse configuration based on detected project settings." + : "Created initial .reliverse configuration. Please review and adjust as needed.", + ); +} + +function mergeWithDefaults( + partialConfig: Partial, +): ReliverseConfig { + return { + ...DEFAULT_CONFIG, + ...partialConfig, + codeStyle: { + ...DEFAULT_CONFIG.codeStyle, + ...(partialConfig.codeStyle ?? {}), + }, + features: { + ...DEFAULT_CONFIG.features, + ...(partialConfig.features ?? {}), + }, + preferredLibraries: { + ...DEFAULT_CONFIG.preferredLibraries, + ...(partialConfig.preferredLibraries ?? {}), + }, + monorepo: { + type: (partialConfig.monorepo?.type ?? DEFAULT_MONOREPO.type) as + | "none" + | "turborepo" + | "nx" + | "pnpm", + packages: partialConfig.monorepo?.packages ?? DEFAULT_MONOREPO.packages, + sharedPackages: + partialConfig.monorepo?.sharedPackages ?? + DEFAULT_MONOREPO.sharedPackages, + }, + }; +} + +async function parseAndFixConfig( + projectName: string, + configPath: string, + cwd: string, +): Promise { + const rawConfig = await fs.readFile(configPath, "utf-8"); + + try { + const parsedConfig = parseJSONC(rawConfig); + if (parsedConfig && typeof parsedConfig === "object") { + const updatedConfig = mergeWithDefaults( + parsedConfig as Partial, + ); + const success = await updateReliverseConfig( + projectName, + configPath, + updatedConfig, + cwd, + ); + + if (success) { + relinka("info", "Fixed .reliverse configuration."); + return await readReliverseConfig(projectName, configPath, cwd); + } + } + } catch (parseError) { + relinka( + "warn", + "Failed to parse .reliverse config:", + parseError instanceof Error ? parseError.message : String(parseError), + ); + } + + return null; +} + +async function regenerateConfig( + projectName: string, + configPath: string, + cwd: string, + githubUsername: string, +): Promise { + await fs.remove(configPath); + relinka("warn", "Found invalid .reliverse config, regenerating..."); + + const defaultRules = await generateDefaultRulesForProject(cwd); + await generateReliverseConfig({ + projectName: defaultRules?.projectName ?? path.basename(cwd), + frontendUsername: defaultRules?.projectAuthor ?? "user", + deployService: "vercel", + primaryDomain: defaultRules?.projectDomain ?? "", + projectPath: cwd, + i18nShouldBeEnabled: defaultRules?.features?.i18n ?? false, + githubUsername, + }); + + return await readReliverseConfig(projectName, configPath, cwd); +} + +export async function verifyReliverseConfig( + cwd: string, + configPath: string, + githubUsername: string, + projectName: string, +): Promise { + try { + // Try to read existing config + let config = await readReliverseConfig(projectName, configPath, cwd); + + // If config is invalid, try to fix it + if (!config) { + config = await parseAndFixConfig(projectName, configPath, cwd); + } + + // If still invalid, regenerate from scratch + if (!config) { + config = await regenerateConfig( + projectName, + configPath, + cwd, + githubUsername, + ); + } + + if (!config) { + throw new Error("Failed to create valid .reliverse configuration"); + } + + return config; + } catch (error) { + relinka( + "error", + "Error reading .reliverse config:", + error instanceof Error ? error.message : String(error), + ); + throw error; + } +} + +export function getReliverseConfigPath(cwd: string) { + return path.join(cwd, ".reliverse"); +} + +export async function getReliverseConfig( + cwd: string, + projectName = "project", + githubUsername = "user", +): Promise { + try { + const configPath = getReliverseConfigPath(cwd); + await createReliverseConfig(cwd, githubUsername); + + const config = await verifyReliverseConfig( + cwd, + configPath, + githubUsername, + projectName, + ); + if (!config) { + throw new Error("Failed to create valid .reliverse configuration"); + } + return config; + } catch (error) { + relinka( + "error", + "Failed to handle .reliverse config:", + error instanceof Error ? error.message : String(error), + ); + throw error; + } +} + +export async function parseCodeStyleFromConfigs( + cwd: string, +): Promise> { + const codeStyle: any = {}; + + // Try to read Prettier config + let prettierConfig: any = {}; + try { + const prettierPath = path.join(cwd, ".prettierrc"); + if (await fs.pathExists(prettierPath)) { + prettierConfig = safeDestr(await fs.readFile(prettierPath, "utf-8")); + } + } catch (error) { + relinka( + "warn-verbose", + "Error parsing Prettier config:", + error instanceof Error ? error.message : String(error), + ); + } + + // Try to read TypeScript config + try { + const tsConfigPath = path.join(cwd, "tsconfig.json"); + if (await fs.pathExists(tsConfigPath)) { + const tsConfig = safeDestr( + await fs.readFile(tsConfigPath, "utf-8"), + ); + + if (tsConfig?.compilerOptions) { + const { compilerOptions } = tsConfig; + + // Detect strict mode settings + codeStyle.strictMode = { + enabled: compilerOptions.strict ?? false, + noImplicitAny: compilerOptions.noImplicitAny ?? false, + strictNullChecks: compilerOptions.strictNullChecks ?? false, + }; + + // Detect module settings + if ( + (compilerOptions.module as string)?.toLowerCase().includes("node") + ) { + codeStyle.importOrRequire = "esm"; + } + } + } + } catch (error) { + relinka( + "warn-verbose", + "Error parsing TypeScript config:", + error instanceof Error ? error.message : String(error), + ); + } + + return { + codeStyle: { + lineWidth: prettierConfig?.printWidth ?? 80, + indentSize: prettierConfig?.tabWidth ?? 2, + indentStyle: prettierConfig?.useTabs ? "tab" : "space", + quoteMark: prettierConfig?.singleQuote ? "single" : "double", + semicolons: prettierConfig?.semi ?? true, + trailingComma: prettierConfig?.trailingComma ?? "all", + bracketSpacing: prettierConfig?.bracketSpacing ?? true, + arrowParens: prettierConfig?.arrowParens ?? "always", + tabWidth: prettierConfig?.tabWidth ?? 2, + jsToTs: false, + }, + }; +} + +export async function revalidateReliverseJson(cwd: string, rulesPath: string) { + // Read file content and continue with the rest of the function... + const fileContent = await fs.readFile(rulesPath, "utf-8"); + const parsedContent = fileContent.trim() + ? destr>(fileContent) + : {}; + + // Get default rules based on project type + const projectType = await detectProjectType(cwd); + const defaultRules = projectType + ? await generateDefaultRulesForProject(cwd) + : await getDefaultReliverseConfig( + path.basename(cwd), + "user", + "nextjs", // fallback default + ); + + if (defaultRules) { + // Parse code style from existing config files + const configRules = await parseCodeStyleFromConfigs(cwd); + + // Always merge with defaults to ensure all fields exist + const mergedRules = { + // Start with user's values + ...parsedContent, + + // Only add defaults for missing fields + projectName: parsedContent.projectName ?? defaultRules.projectName ?? "", + projectAuthor: + parsedContent.projectAuthor ?? defaultRules.projectAuthor ?? "", + projectDescription: + parsedContent.projectDescription ?? + defaultRules.projectDescription ?? + "", + projectVersion: + parsedContent.projectVersion ?? defaultRules.projectVersion ?? "0.1.0", + projectLicense: + parsedContent.projectLicense ?? defaultRules.projectLicense ?? "MIT", + projectRepository: + parsedContent.projectRepository ?? defaultRules.projectRepository ?? "", + + // Project features - only merge if missing + features: { + ...defaultRules.features, + ...parsedContent.features, + }, + + // Development preferences - only set if missing + projectFramework: + parsedContent.projectFramework ?? defaultRules.projectFramework, + projectPackageManager: + parsedContent.projectPackageManager ?? + defaultRules.projectPackageManager, + projectFrameworkVersion: + parsedContent.projectFrameworkVersion ?? + defaultRules.projectFrameworkVersion, + nodeVersion: parsedContent.nodeVersion ?? defaultRules.nodeVersion, + runtime: parsedContent.runtime ?? defaultRules.runtime, + monorepo: parsedContent.monorepo ?? defaultRules.monorepo, + + // Merge nested objects only if missing + preferredLibraries: { + ...defaultRules.preferredLibraries, + ...parsedContent.preferredLibraries, + }, + codeStyle: parsedContent.codeStyle + ? { + ...defaultRules.codeStyle, + ...configRules?.codeStyle, + ...parsedContent.codeStyle, + } + : undefined, + + // Generation preferences - only set if missing + skipPromptsUseAutoBehavior: + parsedContent.skipPromptsUseAutoBehavior ?? + defaultRules.skipPromptsUseAutoBehavior ?? + false, + deployBehavior: + parsedContent.deployBehavior ?? defaultRules.deployBehavior ?? "prompt", + depsBehavior: + parsedContent.depsBehavior ?? defaultRules.depsBehavior ?? "prompt", + gitBehavior: + parsedContent.gitBehavior ?? defaultRules.gitBehavior ?? "prompt", + i18nBehavior: + parsedContent.i18nBehavior ?? defaultRules.i18nBehavior ?? "prompt", + scriptsBehavior: + parsedContent.scriptsBehavior ?? + defaultRules.scriptsBehavior ?? + "prompt", + + // Dependencies management + ignoreDependencies: + parsedContent.ignoreDependencies ?? defaultRules.ignoreDependencies, + + // Custom rules + customRules: { + ...defaultRules.customRules, + ...parsedContent.customRules, + }, + }; + + // Only write if there were missing fields or different values + const currentContent = JSON.stringify(mergedRules); + const originalContent = JSON.stringify(parsedContent); + + if (currentContent !== originalContent) { + // Check for new fields in the default config + const hasNewFields = Object.keys(defaultRules).some((key) => { + const typedKey = key as keyof ReliverseConfig; + return ( + !(typedKey in mergedRules) && defaultRules[typedKey] !== undefined + ); + }); + + // Check for new fields in the merged config + const hasChangedFields = Object.keys(mergedRules).some((key) => { + const typedKey = key as keyof ReliverseConfig; + return ( + typedKey in defaultRules && + defaultRules[typedKey] !== mergedRules[typedKey] + ); + }); + + if (hasNewFields || hasChangedFields) { + await writeReliverseConfig(cwd, mergedRules); + relinka( + "info", + "Updated .reliverse with missing configurations. Please review and adjust as needed.", + ); + } + } + } +} + +async function getPackageJson( + projectPath: string, +): Promise { + try { + return await readPackageJSON(projectPath); + } catch (error) { + relinka( + "warn", + "Could not read package.json:", + error instanceof Error ? error.message : String(error), + ); + return null; + } +} + +async function detectFeatures( + projectPath: string, + packageJson: PackageJson | null, + i18nShouldBeEnabled: boolean, +): Promise { + const deps = { + ...(packageJson?.dependencies ?? {}), + ...(packageJson?.devDependencies ?? {}), + } as Record; + + const hasNextAuth = "next-auth" in deps; + const hasClerk = "@clerk/nextjs" in deps; + const hasPrisma = "@prisma/client" in deps; + const hasDrizzle = "drizzle-orm" in deps; + const hasI18n = + "next-intl" in deps || "react-i18next" in deps || i18nShouldBeEnabled; + const hasAnalytics = + "@vercel/analytics" in deps || "@segment/analytics-next" in deps; + const hasDocker = await fs.pathExists(path.join(projectPath, "Dockerfile")); + const hasCI = + (await fs.pathExists(path.join(projectPath, ".github/workflows"))) || + (await fs.pathExists(path.join(projectPath, ".gitlab-ci.yml"))); + const hasTesting = + "jest" in deps || "vitest" in deps || "@testing-library/react" in deps; + + return { + i18n: hasI18n, + analytics: hasAnalytics, + themeMode: "dark-light" as const, + authentication: hasNextAuth || hasClerk, + api: true, + database: hasPrisma || hasDrizzle, + testing: hasTesting, + docker: hasDocker, + ci: hasCI, + commands: [], + webview: [], + language: ["typescript"], + themes: ["default"], + }; +} + +export async function generateReliverseConfig({ + projectName, + frontendUsername, + deployService, + primaryDomain, + projectPath, + i18nShouldBeEnabled, + overwrite = false, + githubUsername, +}: GenerateReliverseConfigOptions): Promise { + // Read and process package.json + const packageJson = await getPackageJson(projectPath); + + // Get base rules with detected project info + const rules = await getDefaultReliverseConfig( + projectName, + frontendUsername, + packageJson?.type === "module" ? "nextjs" : "nextjs", + ); + + // Configure project details with package.json data when available + rules.projectName = projectName; + rules.projectAuthor = frontendUsername; + rules.projectDescription = + packageJson?.description ?? rules.projectDescription; + rules.projectVersion = packageJson?.version ?? rules.projectVersion; + rules.projectLicense = packageJson?.license ?? rules.projectLicense; + rules.projectRepository = packageJson?.repository + ? typeof packageJson.repository === "string" + ? packageJson.repository + : packageJson.repository.url + : `https://github.com/${githubUsername}/${projectName}`; + rules.projectDeployService = deployService; + rules.projectDomain = primaryDomain + ? `https://${primaryDomain}` + : `https://${projectName}.vercel.app`; + + // Configure features based on detected capabilities + rules.features = await detectFeatures( + projectPath, + packageJson, + i18nShouldBeEnabled, + ); + + // Configure behavior preferences + rules.gitBehavior = "prompt"; + rules.deployBehavior = "prompt"; + rules.depsBehavior = "prompt"; + rules.i18nBehavior = "prompt"; + rules.scriptsBehavior = "prompt"; + rules.skipPromptsUseAutoBehavior = false; + + // Configure code style preferences + rules.codeStyle = { + ...rules.codeStyle, + dontRemoveComments: true, + shouldAddComments: true, + typeOrInterface: "type", + importOrRequire: "import", + quoteMark: "double", + semicolons: true, + lineWidth: 80, + indentStyle: "space", + indentSize: 2, + importSymbol: "~", + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + tabWidth: 2, + jsToTs: false, + cjsToEsm: false, + modernize: { + replaceFs: false, + replacePath: false, + replaceHttp: false, + replaceProcess: false, + replaceConsole: false, + replaceEvents: false, + }, + }; + + // Write config file + const configPath = path.join(projectPath, ".reliverse"); + + // Read existing content if any + let existingContent: ReliverseConfig | null = null; + if (!overwrite && (await fs.pathExists(configPath))) { + try { + const content = await fs.readFile(configPath, "utf-8"); + existingContent = destr(content); + } catch { + // Ignore read errors + } + } + + // Merge with existing content if any + const configContent = { + ...DEFAULT_CONFIG, + ...existingContent, + ...rules, + }; + + await writeReliverseConfig(projectPath, configContent); +} + +async function checkProjectFiles(projectPath: string): Promise<{ + hasReliverse: boolean; + hasPackageJson: boolean; + hasNodeModules: boolean; + hasGit: boolean; +}> { + const [hasReliverse, hasPackageJson, hasNodeModules, hasGit] = + await Promise.all([ + fs.pathExists(path.join(projectPath, ".reliverse")), + fs.pathExists(path.join(projectPath, "package.json")), + fs.pathExists(path.join(projectPath, "node_modules")), + fs.pathExists(path.join(projectPath, ".git")), + ]); + + return { hasReliverse, hasPackageJson, hasNodeModules, hasGit }; +} + +export async function detectProject( + projectPath: string, +): Promise { + try { + const { hasReliverse, hasPackageJson, hasNodeModules, hasGit } = + await checkProjectFiles(projectPath); + + if (!hasReliverse || !hasPackageJson) { + return null; + } + + const configContent = await fs.readFile( + path.join(projectPath, ".reliverse"), + "utf-8", + ); + const parsedConfig = parseJSONC(configContent); + const config = destr(parsedConfig); + + return { + name: path.basename(projectPath), + path: projectPath, + config, + needsDepsInstall: !hasNodeModules, + hasGit, + }; + } catch (error) { + relinka( + "warn", + `Error processing ${projectPath}: ${error instanceof Error ? error.message : String(error)}`, + ); + return null; + } +} + +export async function detectProjectsWithReliverse( + cwd: string, +): Promise { + const detectedProjects: DetectedProject[] = []; + + // First check the root directory + const rootProject = await detectProject(cwd); + if (rootProject) { + detectedProjects.push(rootProject); + } + + // Then check subdirectories + try { + const items = await fs.readdir(cwd, { withFileTypes: true }); + for (const item of items) { + if (item.isDirectory()) { + const projectPath = path.join(cwd, item.name); + const project = await detectProject(projectPath); + if (project) { + detectedProjects.push(project); + } + } + } + } catch (error) { + relinka( + "warn", + `Error reading directory ${cwd}: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + return detectedProjects; +} diff --git a/src/app/app-utils.ts b/src/utils/reliverseMemory.ts similarity index 98% rename from src/app/app-utils.ts rename to src/utils/reliverseMemory.ts index 7711c58c..b6f0ad76 100644 --- a/src/app/app-utils.ts +++ b/src/utils/reliverseMemory.ts @@ -9,7 +9,7 @@ import { MEMORY_FILE } from "~/app/constants.js"; import { db } from "~/app/db/client.js"; import { encrypt, decrypt } from "~/app/db/config.js"; import { configKeysTable, userDataTable } from "~/app/db/schema.js"; -import { relinka } from "~/app/menu/create-project/cp-modules/cli-main-modules/handlers/logger.js"; +import { relinka } from "~/utils/loggerRelinka.js"; export async function getReliverseMemory(): Promise { // Ensure directory exists