From 7b3e07cf9afc80e254e12595a69f485a64383a8d Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Fri, 14 Aug 2026 23:37:37 +0500 Subject: [PATCH 1/2] fix: avoid install prompt in non-interactive environments --- package.json | 2 +- pnpm-lock.yaml | 2 +- src/utils/dependencies.ts | 13 +++++++---- test/unit/ensurePackageInstalled.test.ts | 29 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 test/unit/ensurePackageInstalled.test.ts diff --git a/package.json b/package.json index d8fed9fd5..920744dbd 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "@electric-sql/pglite": "*", "@libsql/client": "*", "@valibot/to-json-schema": "^1.5.0", - "better-sqlite3": "^12.5.0", + "better-sqlite3": "^12.5.0 || ^13.0.0", "sqlite3": "*", "valibot": "^1.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74bc2b2e9..4ac91585d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,7 +38,7 @@ importers: specifier: ^1.1.1 version: 1.1.1 better-sqlite3: - specifier: ^12.5.0 + specifier: ^12.5.0 || ^13.0.0 version: 12.10.0 c12: specifier: ^3.3.4 diff --git a/src/utils/dependencies.ts b/src/utils/dependencies.ts index 2892d3ab4..a53340c22 100644 --- a/src/utils/dependencies.ts +++ b/src/utils/dependencies.ts @@ -1,5 +1,6 @@ import { addDependency } from 'nypm' import { resolvePackageJSON } from 'pkg-types' +import { hasTTY, isCI } from 'std-env' import { logger } from './dev' import nuxtContentContext from './context' import { tryUseNuxt } from '@nuxt/kit' @@ -22,11 +23,13 @@ export async function ensurePackageInstalled(pkg: string) { if (!await isPackageInstalled(pkg)) { logger.error(`Nuxt Content requires \`${pkg}\` module to operate.`) - const confirm = await logger.prompt(`Do you want to install \`${pkg}\` package?`, { - type: 'confirm', - name: 'confirm', - initial: true, - }) + const confirm = hasTTY && !isCI + ? await logger.prompt(`Do you want to install \`${pkg}\` package?`, { + type: 'confirm', + name: 'confirm', + initial: true, + }) + : false if (!confirm) { logger.error(`Nuxt Content requires \`${pkg}\` module to operate. Please install \`${pkg}\` package manually and try again. \`npm install ${pkg}\``) diff --git a/test/unit/ensurePackageInstalled.test.ts b/test/unit/ensurePackageInstalled.test.ts new file mode 100644 index 000000000..eb583ef7f --- /dev/null +++ b/test/unit/ensurePackageInstalled.test.ts @@ -0,0 +1,29 @@ +import { afterEach, describe, expect, test, vi } from 'vitest' + +const logger = { error: vi.fn(), prompt: vi.fn() } +const addDependency = vi.fn() + +vi.mock('std-env', async importOriginal => ({ + ...(await importOriginal()), + hasTTY: false, + isCI: true, +})) +vi.mock('../../src/utils/dev', () => ({ logger })) +vi.mock('nypm', () => ({ addDependency })) + +describe('ensurePackageInstalled', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + + test('reports how to install the package instead of prompting when there is no TTY', async () => { + const exit = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never) + const { ensurePackageInstalled } = await import('../../src/utils/dependencies') + + await ensurePackageInstalled('@nuxt/content-not-installed') + + expect(logger.prompt).not.toHaveBeenCalled() + expect(logger.error).toHaveBeenLastCalledWith(expect.stringContaining('npm install @nuxt/content-not-installed')) + expect(exit).toHaveBeenCalledWith(1) + }) +}) From 0143e732990fd77cde0c6cf7d04127eb0fc6466f Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Sat, 15 Aug 2026 00:48:10 +0500 Subject: [PATCH 2/2] test: split hasTTY/isCI cases in ensurePackageInstalled --- test/unit/ensurePackageInstalled.test.ts | 41 +++++++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/test/unit/ensurePackageInstalled.test.ts b/test/unit/ensurePackageInstalled.test.ts index eb583ef7f..af9314cf1 100644 --- a/test/unit/ensurePackageInstalled.test.ts +++ b/test/unit/ensurePackageInstalled.test.ts @@ -3,20 +3,21 @@ import { afterEach, describe, expect, test, vi } from 'vitest' const logger = { error: vi.fn(), prompt: vi.fn() } const addDependency = vi.fn() -vi.mock('std-env', async importOriginal => ({ - ...(await importOriginal()), - hasTTY: false, - isCI: true, -})) vi.mock('../../src/utils/dev', () => ({ logger })) vi.mock('nypm', () => ({ addDependency })) describe('ensurePackageInstalled', () => { afterEach(() => { vi.restoreAllMocks() + vi.resetModules() }) test('reports how to install the package instead of prompting when there is no TTY', async () => { + vi.doMock('std-env', async importOriginal => ({ + ...(await importOriginal()), + hasTTY: false, + isCI: false, + })) const exit = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never) const { ensurePackageInstalled } = await import('../../src/utils/dependencies') @@ -26,4 +27,34 @@ describe('ensurePackageInstalled', () => { expect(logger.error).toHaveBeenLastCalledWith(expect.stringContaining('npm install @nuxt/content-not-installed')) expect(exit).toHaveBeenCalledWith(1) }) + + test('reports how to install the package instead of prompting in CI', async () => { + vi.doMock('std-env', async importOriginal => ({ + ...(await importOriginal()), + hasTTY: true, + isCI: true, + })) + const exit = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never) + const { ensurePackageInstalled } = await import('../../src/utils/dependencies') + + await ensurePackageInstalled('@nuxt/content-not-installed') + + expect(logger.prompt).not.toHaveBeenCalled() + expect(logger.error).toHaveBeenLastCalledWith(expect.stringContaining('npm install @nuxt/content-not-installed')) + expect(exit).toHaveBeenCalledWith(1) + }) + + test('prompts to install the package when interactive and not in CI', async () => { + vi.doMock('std-env', async importOriginal => ({ + ...(await importOriginal()), + hasTTY: true, + isCI: false, + })) + vi.spyOn(process, 'exit').mockImplementation(() => undefined as never) + const { ensurePackageInstalled } = await import('../../src/utils/dependencies') + + await ensurePackageInstalled('@nuxt/content-not-installed') + + expect(logger.prompt).toHaveBeenCalled() + }) })