From 4881f34ea6ada3e1e07e64b19f57dbcbbc5535a0 Mon Sep 17 00:00:00 2001 From: SakshiKoli-CS Date: Tue, 3 Nov 2026 12:12:09 +0530 Subject: [PATCH] fix(prompts): start every command on csdx 2.x, whose inquirer 12 broke registerSearchList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit csdx 2.x's bin/run.js makes every plugin resolve @contentstack/cli-utilities to csdx's own 2.x copy, which ships inquirer 12. registerSearchList(), run by every command at init, assumed inquirer 8: it called a top-level registerPrompt, which inquirer 12 only has on its default export, and loaded inquirer/lib/prompts/{list,checkbox}, which inquirer 12's package exports hide. So every launch:* command failed on csdx 2.x with "inquirer.registerPrompt is not a function". registerSearchList now registers on whichever of the module or its default export has registerPrompt, and keeps inquirer's own list and checkbox when their lib/prompts files are absent or not exported. Any other load error still throws. On csdx 1.x nothing changes. Refs CL-8328 #claude_code# 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/core/search-list.test.ts | 38 ++++++++++++++++++++++++++++++++++++ src/core/search-list.ts | 31 +++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/core/search-list.test.ts b/src/core/search-list.test.ts index e0f5d818..c89989cc 100644 --- a/src/core/search-list.test.ts +++ b/src/core/search-list.test.ts @@ -361,6 +361,44 @@ describe('registerSearchList', () => { expect(registered[0][1]).not.toBe(SearchList); }); + it('registers on the default export of an inquirer that has no top-level registerPrompt, as cli-utilities 2 ships', () => { + const registered: string[] = []; + const fakeLoad = (id: string): unknown => + id === 'inquirer' ? { default: { registerPrompt: (name: string) => registered.push(name) } } : SearchList; + + registerSearchList(fakeLoad); + + expect(registered).toEqual(['search-list', 'list', 'checkbox']); + }); + + it.each([ + ['its package exports hide lib/prompts, as inquirer 12 does', 'ERR_PACKAGE_PATH_NOT_EXPORTED'], + ['lib/prompts is missing from the package', 'MODULE_NOT_FOUND'], + ])('keeps the built-in list and checkbox when %s', (_reason, code) => { + const registered: string[] = []; + const fakeLoad = (id: string): unknown => { + if (id.startsWith('inquirer/lib/prompts/')) { + throw Object.assign(new Error(`Cannot load '${id}'`), { code }); + } + return id === 'inquirer' ? { default: { registerPrompt: (name: string) => registered.push(name) } } : SearchList; + }; + + expect(() => registerSearchList(fakeLoad)).not.toThrow(); + expect(registered).toEqual(['search-list']); + }); + + it('still fails when a list or checkbox prompt cannot load for any reason other than being absent', () => { + const broken = new Error('Unexpected token in inquirer/lib/prompts/list'); + const fakeLoad = (id: string): unknown => { + if (id === 'inquirer/lib/prompts/list') { + throw broken; + } + return id === 'inquirer' ? { registerPrompt: () => undefined } : SearchList; + }; + + expect(() => registerSearchList(fakeLoad)).toThrow(broken); + }); + it('registers on the real inquirer cli-utilities depends on when no loader is given', () => { const inquirer = load('inquirer') as { prompt: { prompts: Record } }; diff --git a/src/core/search-list.ts b/src/core/search-list.ts index b9b6d93d..76070a1c 100644 --- a/src/core/search-list.ts +++ b/src/core/search-list.ts @@ -153,12 +153,39 @@ export function utilitiesLoader(): ModuleLoader { return createRequire(require.resolve('@contentstack/cli-utilities')); } +// cli-utilities 1 ships inquirer 8 (registerPrompt on the module, prompts under lib/prompts). csdx 2 forces +// every plugin onto its own cli-utilities 2, whose inquirer 12 has registerPrompt only on the default export +// and no lib/prompts, so list and checkbox keep inquirer's own paging there. +function promptRegistry(inquirer: unknown): PromptRegistry { + const module = inquirer as Partial & { default?: PromptRegistry }; + + return typeof module.registerPrompt === 'function' ? (module as PromptRegistry) : (module.default as PromptRegistry); +} + +const ABSENT_MODULE_CODES = new Set(['MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED']); + +function loadIfPresent(load: ModuleLoader, id: string): unknown { + try { + return load(id); + } catch (error) { + if (ABSENT_MODULE_CODES.has(String((error as { code?: unknown }).code))) { + return undefined; + } + + throw error; + } +} + export function registerSearchList(load: ModuleLoader = utilitiesLoader()): void { - const inquirer = load('inquirer') as PromptRegistry; + const inquirer = promptRegistry(load('inquirer')); inquirer.registerPrompt('search-list', launchSearchList(load('inquirer-search-list') as SearchListClass)); for (const type of ['list', 'checkbox']) { - inquirer.registerPrompt(type, launchPaginated(load(`inquirer/lib/prompts/${type}`) as PaginatedClass)); + const Prompt = loadIfPresent(load, `inquirer/lib/prompts/${type}`); + + if (Prompt !== undefined) { + inquirer.registerPrompt(type, launchPaginated(Prompt as PaginatedClass)); + } } }