Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/cli/src/commands/docs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as p from '@clack/prompts';
import { validateInstallationOptions } from '@/utils/installation/codegen';
import { isRendererValidForUseCase } from '@/utils/installation/detect-renderer';
import { RENDERER_LABELS } from '@/utils/installation/renderer-options';
import type { InstallMethod, Renderer, UseCase } from '@/utils/installation/types';
import { type InstallMethod, type Renderer, type UseCase, VALID_RENDERERS } from '@/utils/installation/types';
import type { Framework } from '../utils/config.js';
import { getConfigValue } from '../utils/config.js';
import { docExistsInAnyFramework, readBundledDoc, readLlmsTxt } from '../utils/docs.js';
Expand Down Expand Up @@ -59,6 +60,12 @@ function mapPresetToUseCase(preset: string): UseCase {
return result;
}

const PRESET_NAMES: Record<UseCase, string> = {
'default-video': 'video',
'default-audio': 'audio',
'background-video': 'background-video',
};

const ALL_RENDERERS = Object.keys(RENDERER_LABELS) as Renderer[];

function validateMedia(media: string): Renderer {
Expand Down Expand Up @@ -184,6 +191,17 @@ export async function handleDocs(flags: ParsedFlags, positionals: string[]): Pro
process.exit(1);
}

// The interactive prompt builds its renderer list from VALID_RENDERERS[useCase],
// so an invalid renderer/use-case pairing can only arrive via the explicit
// `--media` flag. Reject it here to keep the CLI at parity with the UI.
if (!isRendererValidForUseCase(opts.renderer, opts.useCase)) {
const validMedia = VALID_RENDERERS[opts.useCase].join(', ');
console.error(
`Error: media type "${opts.renderer}" is not valid for the "${PRESET_NAMES[opts.useCase]}" preset. Valid options: ${validMedia}`
);
process.exit(1);
}

// The interactive prompt hides CDN for renderers without a CDN build; guard
// the non-interactive flag path so a `--install-method cdn` request for one
// can't emit a broken snippet.
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/commands/tests/docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,22 @@ describe('handleDocs', () => {
).rejects.toThrow(ExitError);
expect(errors()).toContain('no CDN build');
});

it('errors when --media is not valid for the chosen --preset (dash + audio)', async () => {
await expect(
handleDocs(htmlFlags({ preset: 'audio', skin: 'default', media: 'dash' }), ['how-to/installation'])
).rejects.toThrow(ExitError);
const err = errors();
expect(err).toContain('media type "dash" is not valid for the "audio" preset');
expect(err).toContain('html5-audio');
});

it('errors when --media is an audio-only renderer for the video preset (mux-audio + video)', async () => {
await expect(
handleDocs(htmlFlags({ preset: 'video', skin: 'default', media: 'mux-audio' }), ['how-to/installation'])
).rejects.toThrow(ExitError);
expect(errors()).toContain('media type "mux-audio" is not valid for the "video" preset');
});
});

describe('React framework', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/site-modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ declare module '@/utils/installation/detect-renderer' {
}

export function detectRenderer(url: string, useCase: UseCase): DetectionResult | null;
export function isRendererValidForUseCase(renderer: Renderer, useCase: UseCase): boolean;
}

declare module '@/utils/installation/cdn-code' {
Expand Down
Loading