Files
open-design/apps/daemon/src/export-cli-request.ts
7a37c1462d feat(export): add reliable standalone HTML export (#6855)
* feat(export): add standalone HTML export

Bundle project-local HTML, CSS, JavaScript, images, fonts, workers,
and iframe dependencies into one offline-safe file.

Expose the same contract through the web download flow and od CLI,
with structured failures and bounded output assembly.

* fix(daemon): honor standalone source URL semantics

Resolve document assets against local base href values, reject browser-invalid TypeScript and JSX sources with a structured 422, and count unquoted first-level resources before graph traversal.

Validated with daemon tests, workspace typecheck, and guard.

* fix(daemon): handle standalone data scripts and CSS URL tokens

Preserve non-JavaScript script payloads as data blocks and scan CSS values without treating strings or comments as project dependencies.

Validated with focused and full daemon tests, workspace typecheck, and guard.

* fix(daemon): classify standalone export resources correctly

Preserve non-HTML iframe MIME types, inline only resource-bearing link relations, and propagate unexpected Vite dist read failures instead of silently falling back.

Validated with focused export tests, the full daemon suite, guard, and workspace typecheck.

* fix(export): preserve version and stylesheet semantics

Hide standalone HTML export for historical versions until dependency snapshots are available, avoiding an action that always fails.

Keep disabled and alternate stylesheets as embedded link elements so standalone export preserves activation semantics. Validated with focused red/green coverage, the full web suite, pnpm guard, and pnpm typecheck.

---------

Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>
2026-08-14 07:21:21 +00:00

61 lines
1.9 KiB
TypeScript

import type { ExportFormat, ExportImageFormat, ExportResult } from "@open-design/contracts";
export interface ExportCliRequestOptions {
fileName: string;
format: ExportFormat;
deck?: boolean;
imageFormat?: ExportImageFormat;
title?: string;
}
export interface ExportCliDeckModeOptions {
format: ExportFormat;
deck?: boolean;
page?: boolean;
noDeck?: boolean;
}
export function resolveExportCliDeckMode(options: ExportCliDeckModeOptions): boolean | undefined {
const explicitDeck = options.deck === true;
const explicitPage = options.page === true || options.noDeck === true;
if (explicitDeck && explicitPage) {
throw new Error('--deck cannot be combined with --page or --no-deck');
}
if (options.format === "html" && (explicitDeck || explicitPage)) {
throw new Error('--deck/--page/--no-deck is not valid with --format html');
}
if (options.format === "pptx") {
if (explicitPage) throw new Error('--page/--no-deck is not valid with --format pptx');
return true;
}
if (explicitDeck) return true;
if (explicitPage) return false;
return undefined;
}
export function buildExportCliRequestBody(options: ExportCliRequestOptions): Record<string, unknown> {
const deck = options.format === "pptx" ? true : options.deck;
return {
fileName: options.fileName,
// PPTX is deck-only. For PDF/image, omit `deck` unless the caller explicitly
// chooses deck/page mode so the daemon can still auto-detect by default.
...(deck !== undefined ? { deck } : {}),
...(options.format === "image" && options.imageFormat ? { imageFormat: options.imageFormat } : {}),
...(options.title ? { title: options.title } : {}),
};
}
export function buildExportCliResultEnvelope(options: {
bytes: number;
format: ExportFormat;
path: string;
}): ExportResult {
return {
ok: true,
path: options.path,
out: options.path,
bytes: options.bytes,
format: options.format,
};
}