From 472aee4fdc434b8386e7293b9ea372642dc0b610 Mon Sep 17 00:00:00 2001 From: Connor van Spronssen Date: Mon, 5 Aug 2024 14:02:20 +0200 Subject: [PATCH 1/4] fix: Add a warning message when the package manager cannot be determined (#29) --- src/package-utils/detect-pm.ts | 2 ++ src/package-utils/log-helpers.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/package-utils/detect-pm.ts b/src/package-utils/detect-pm.ts index cc7e57c..fc9f361 100644 --- a/src/package-utils/detect-pm.ts +++ b/src/package-utils/detect-pm.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { existsSync } from "fs"; +import { logWarning } from "./log-helpers"; type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; @@ -25,6 +26,7 @@ function detectPackageManager(): PackageManager { } // Default to npm if none of the above are found + logWarning("Could not find any package manager files. Defaulting to npm."); return "npm"; } diff --git a/src/package-utils/log-helpers.ts b/src/package-utils/log-helpers.ts index 0cc97eb..d70e207 100644 --- a/src/package-utils/log-helpers.ts +++ b/src/package-utils/log-helpers.ts @@ -4,6 +4,10 @@ export function logSuccess(message: string) { console.log(chalk.green(`✔ ${message}`)); } +export function logWarning(message: string) { + console.warn(chalk.yellow(`⚠️ ${message}`)); +} + export function logError(message: string) { console.error(chalk.red(`✘ ${message}`)); } From 6b5ac909d3c6339eac019e015360e6e80b4def52 Mon Sep 17 00:00:00 2001 From: Connor van Spronssen Date: Mon, 5 Aug 2024 14:40:16 +0200 Subject: [PATCH 2/4] fix: Look for lockfiles in parent directories if workspaces are being used (#29) --- src/package-utils/detect-pm.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/package-utils/detect-pm.ts b/src/package-utils/detect-pm.ts index fc9f361..77f821f 100644 --- a/src/package-utils/detect-pm.ts +++ b/src/package-utils/detect-pm.ts @@ -1,27 +1,41 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { existsSync } from "fs"; import { logWarning } from "./log-helpers"; +import path, { dirname } from "path"; type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; function detectPackageManager(): PackageManager { + let projectRoot = process.cwd(); + + // Find the project root, in case workspaces are being used. + do { + projectRoot = path.resolve(projectRoot, ".."); + } while (projectRoot !== "/" && !existsSync(`${projectRoot}/package.json`)); + // Check for package-lock.json - if (existsSync("package-lock.json")) { + if ( + existsSync("package-lock.json") || + existsSync(`${projectRoot}/package-lock.json`) + ) { return "npm"; } // Check for yarn.lock - if (existsSync("yarn.lock")) { + if (existsSync("yarn.lock") || existsSync(`${projectRoot}/yarn.lock`)) { return "yarn"; } // Check for pnpm-lock.yaml - if (existsSync("pnpm-lock.yaml")) { + if ( + existsSync("pnpm-lock.yaml") || + existsSync(`${projectRoot}/pnpm-lock.yaml`) + ) { return "pnpm"; } // bun.lockb - if (existsSync("bun.lockb")) { + if (existsSync("bun.lockb") || existsSync(`${projectRoot}/bun.lockb`)) { return "bun"; } From dd579895fc79682aafac3b2f5db19caffd314d23 Mon Sep 17 00:00:00 2001 From: Connor van Spronssen Date: Tue, 13 Aug 2024 13:32:39 +0200 Subject: [PATCH 3/4] feat: add an option to manually specify a package manager (#29) --- src/module.ts | 11 +++++++++-- src/package-utils/detect-pm.ts | 20 +++++++++++++------- src/package-utils/setup-helpers.ts | 21 +++++++++++++-------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/module.ts b/src/module.ts index 9d63325..89cab6f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -23,6 +23,7 @@ import { } from "./package-utils/setup-helpers"; import { log, PREDEFINED_LOG_MESSAGES } from "./package-utils/log-helpers"; import type { Prisma } from "@prisma/client"; +import type { PackageManager } from "./package-utils/detect-pm"; interface ModuleOptions extends Prisma.PrismaClientOptions { writeToSchema: boolean; @@ -33,6 +34,7 @@ interface ModuleOptions extends Prisma.PrismaClientOptions { generateClient: boolean; installStudio: boolean; autoSetupPrisma: boolean; + packageManager?: PackageManager; } export type PrismaExtendedModule = ModuleOptions; @@ -59,6 +61,7 @@ export default defineNuxtModule({ generateClient: true, installStudio: true, autoSetupPrisma: false, + packageManager: undefined, }, async setup(options, nuxt) { @@ -118,7 +121,7 @@ export default defineNuxtModule({ // if Prisma CLI is installed skip the following step. if (!prismaInstalled) { - await installPrismaCLI(PROJECT_PATH); + await installPrismaCLI(PROJECT_PATH, options.packageManager); } } @@ -225,7 +228,11 @@ export default defineNuxtModule({ await writeClientInLib(resolveProject("lib", "prisma.ts")); if (options.generateClient) { - await generateClient(PROJECT_PATH, options.installClient); + await generateClient( + PROJECT_PATH, + options.installClient, + options.packageManager, + ); } await prismaStudioWorkflow(); diff --git a/src/package-utils/detect-pm.ts b/src/package-utils/detect-pm.ts index 77f821f..b7cb7de 100644 --- a/src/package-utils/detect-pm.ts +++ b/src/package-utils/detect-pm.ts @@ -1,14 +1,18 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { existsSync } from "fs"; import { logWarning } from "./log-helpers"; -import path, { dirname } from "path"; +import path from "path"; -type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; +export type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; + +function detectPackageManager(packageManager?: PackageManager): PackageManager { + // If a package manager was explicitly defined, use that one. + if (packageManager) return packageManager; -function detectPackageManager(): PackageManager { let projectRoot = process.cwd(); // Find the project root, in case workspaces are being used. + // Please note that resolveProject will not work, since it picks the layer directory. do { projectRoot = path.resolve(projectRoot, ".."); } while (projectRoot !== "/" && !existsSync(`${projectRoot}/package.json`)); @@ -44,8 +48,8 @@ function detectPackageManager(): PackageManager { return "npm"; } -export const installingPrismaCLIWithPM = () => { - const pm = detectPackageManager(); +export const installingPrismaCLIWithPM = (packageManager?: PackageManager) => { + const pm = detectPackageManager(packageManager); switch (pm) { case "npm": { @@ -81,8 +85,10 @@ export const installingPrismaCLIWithPM = () => { } }; -export const installingPrismaClientWithPM = () => { - const pm = detectPackageManager(); +export const installingPrismaClientWithPM = ( + packageManager?: PackageManager, +) => { + const pm = detectPackageManager(packageManager); switch (pm) { case "npm": { diff --git a/src/package-utils/setup-helpers.ts b/src/package-utils/setup-helpers.ts index e11f293..7340058 100644 --- a/src/package-utils/setup-helpers.ts +++ b/src/package-utils/setup-helpers.ts @@ -2,6 +2,7 @@ import { execa } from "execa"; import { installingPrismaClientWithPM, installingPrismaCLIWithPM, + type PackageManager, } from "./detect-pm"; import { log, @@ -39,9 +40,12 @@ export async function isPrismaCLIInstalled( } } -export async function installPrismaCLI(directory: string) { +export async function installPrismaCLI( + directory: string, + packageManager?: PackageManager, +) { try { - const installCmd = installingPrismaCLIWithPM(); + const installCmd = installingPrismaCLIWithPM(packageManager); await execa(installCmd.pm, installCmd.command, { cwd: directory, @@ -72,19 +76,19 @@ export async function initPrisma({ provider = "sqlite", datasourceUrl, }: PrismaInitOptions) { - const command = ["npx", "prisma", "init", "--datasource-provider"]; + const commandArgs = ["prisma", "init", "--datasource-provider"]; - command.push(provider); + commandArgs.push(provider); if (datasourceUrl) { - command.push("--url"); - command.push(datasourceUrl); + commandArgs.push("--url"); + commandArgs.push(datasourceUrl); } try { log(PREDEFINED_LOG_MESSAGES.initPrisma.action); - const { stdout: initializePrisma } = await execa("npx", command, { + const { stdout: initializePrisma } = await execa("npx", commandArgs, { cwd: directory, }); @@ -174,12 +178,13 @@ export async function formatSchema(directory: string) { export async function generateClient( directory: string, installPrismaClient: boolean = true, + packageManager?: PackageManager, ) { log(PREDEFINED_LOG_MESSAGES.generatePrismaClient.action); if (installPrismaClient) { try { - const installCmd = installingPrismaClientWithPM(); + const installCmd = installingPrismaClientWithPM(packageManager); await execa(installCmd.pm, installCmd.command, { cwd: directory, From 0785f5cbac1a3326c00c0dd5f526bdf731a90286 Mon Sep 17 00:00:00 2001 From: Connor van Spronssen Date: Tue, 13 Aug 2024 16:17:12 +0200 Subject: [PATCH 4/4] feat: Add an option to set a custom Prisma root path (#29) --- src/module.ts | 18 ++++++---- src/package-utils/detect-pm.ts | 10 ++---- src/package-utils/get-project-root.ts | 14 ++++++++ src/package-utils/setup-helpers.ts | 50 +++++++++++++++------------ 4 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 src/package-utils/get-project-root.ts diff --git a/src/module.ts b/src/module.ts index 89cab6f..0662889 100644 --- a/src/module.ts +++ b/src/module.ts @@ -24,6 +24,7 @@ import { import { log, PREDEFINED_LOG_MESSAGES } from "./package-utils/log-helpers"; import type { Prisma } from "@prisma/client"; import type { PackageManager } from "./package-utils/detect-pm"; +import getProjectRoot from "./package-utils/get-project-root"; interface ModuleOptions extends Prisma.PrismaClientOptions { writeToSchema: boolean; @@ -35,6 +36,7 @@ interface ModuleOptions extends Prisma.PrismaClientOptions { installStudio: boolean; autoSetupPrisma: boolean; packageManager?: PackageManager; + prismaRoot?: string; } export type PrismaExtendedModule = ModuleOptions; @@ -62,11 +64,13 @@ export default defineNuxtModule({ installStudio: true, autoSetupPrisma: false, packageManager: undefined, + prismaRoot: undefined, }, async setup(options, nuxt) { const { resolve: resolveProject } = createResolver(nuxt.options.rootDir); const { resolve: resolver } = createResolver(import.meta.url); + const { resolve: resolveRoot } = createResolver(getProjectRoot()); const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url)); // Identifies which script is running: posinstall, dev or prod @@ -113,7 +117,11 @@ export default defineNuxtModule({ return; } - const PROJECT_PATH = resolveProject(); + let projectPath = resolveProject(); + if (options.prismaRoot?.length) + projectPath = resolveRoot(options.prismaRoot); + + const PROJECT_PATH = projectPath; if (options.installCLI) { // Check if Prisma CLI is installed. @@ -177,8 +185,7 @@ export default defineNuxtModule({ }); // Add dummy models to the Prisma schema - await writeToSchema(resolveProject("prisma", "schema.prisma")); - await prismaMigrateWorkflow(); + await writeToSchema(`${PROJECT_PATH}/prisma/schema.prisma`); }; const prismaStudioWorkflow = async () => { @@ -221,11 +228,10 @@ export default defineNuxtModule({ if (!prismaSchemaExists) { await prismaInitWorkflow(); - } else { - await prismaMigrateWorkflow(); } - await writeClientInLib(resolveProject("lib", "prisma.ts")); + await prismaMigrateWorkflow(); + await writeClientInLib(PROJECT_PATH); if (options.generateClient) { await generateClient( diff --git a/src/package-utils/detect-pm.ts b/src/package-utils/detect-pm.ts index b7cb7de..612e464 100644 --- a/src/package-utils/detect-pm.ts +++ b/src/package-utils/detect-pm.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { existsSync } from "fs"; import { logWarning } from "./log-helpers"; -import path from "path"; +import getProjectRoot from "./get-project-root"; export type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; @@ -9,13 +9,7 @@ function detectPackageManager(packageManager?: PackageManager): PackageManager { // If a package manager was explicitly defined, use that one. if (packageManager) return packageManager; - let projectRoot = process.cwd(); - - // Find the project root, in case workspaces are being used. - // Please note that resolveProject will not work, since it picks the layer directory. - do { - projectRoot = path.resolve(projectRoot, ".."); - } while (projectRoot !== "/" && !existsSync(`${projectRoot}/package.json`)); + const projectRoot = getProjectRoot(); // Check for package-lock.json if ( diff --git a/src/package-utils/get-project-root.ts b/src/package-utils/get-project-root.ts new file mode 100644 index 0000000..bf8da81 --- /dev/null +++ b/src/package-utils/get-project-root.ts @@ -0,0 +1,14 @@ +import { existsSync } from "fs"; +import path from "path"; + +export default function getProjectRoot(): string { + let projectRoot = process.cwd(); + + // Find the project root, in case workspaces are being used. + // Please note that resolveProject will not work, since it picks the layer directory. + do { + projectRoot = path.resolve(projectRoot, ".."); + } while (projectRoot !== "/" && !existsSync(`${projectRoot}/package.json`)); + + return projectRoot; +} diff --git a/src/package-utils/setup-helpers.ts b/src/package-utils/setup-helpers.ts index 7340058..99ff953 100644 --- a/src/package-utils/setup-helpers.ts +++ b/src/package-utils/setup-helpers.ts @@ -124,23 +124,26 @@ export async function writeToSchema(prismaSchemaPath: string) { return false; } - const addModel = ` - model User { - id Int @id @default(autoincrement()) - email String @unique - name String? - posts Post[] - } - - model Post { - id Int @id @default(autoincrement()) - title String - content String? - published Boolean @default(false) - author User @relation(fields: [authorId], references: [id]) - authorId Int - } - `; + const addModel = `\ +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +`; + + // Don't bother adding the models if they already exist. + if (existingSchema.trim().includes(addModel.trim())) return; const updatedSchema = `${existingSchema.trim()}\n\n${addModel}`; writeFileSync(prismaSchemaPath, updatedSchema); @@ -235,11 +238,12 @@ export async function installStudio(directory: string) { } export async function writeClientInLib(path: string) { - const existingContent = existsSync(path); + const existingContent = existsSync(`${path}/lib/prisma.ts`); try { if (!existingContent) { - const prismaClient = `import { PrismaClient } from '@prisma/client' + const prismaClient = `\ +import { PrismaClient } from '@prisma/client' const prismaClientSingleton = () => { return new PrismaClient() @@ -256,16 +260,16 @@ export default prisma if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma `; - if (!existsSync("lib")) { - mkdirSync("lib"); + if (!existsSync(`${path}/lib`)) { + mkdirSync(`${path}/lib`); } - if (existsSync("lib/prisma.ts")) { + if (existsSync(`${path}/lib/prisma.ts`)) { log(PREDEFINED_LOG_MESSAGES.writeClientInLib.found); return; } - writeFileSync("lib/prisma.ts", prismaClient); + writeFileSync(`${path}/lib/prisma.ts`, prismaClient); logSuccess(PREDEFINED_LOG_MESSAGES.writeClientInLib.success); }