diff --git a/packages/parser/src/config.ts b/packages/parser/src/config.ts index 40b4e37154..8449abb7e4 100644 --- a/packages/parser/src/config.ts +++ b/packages/parser/src/config.ts @@ -1,5 +1,5 @@ import { toArray, uniq } from '@antfu/utils' -import type { DrawingsOptions, FontOptions, ResolvedDrawingsOptions, ResolvedFontOptions, SlidevConfig, SlidevThemeMeta } from '@slidev/types' +import type { DrawingsOptions, FontOptions, ResolvedDrawingsOptions, ResolvedExportOptions, ResolvedFontOptions, SlidevConfig, SlidevThemeMeta } from '@slidev/types' import { parseAspectRatio } from './utils' export function resolveConfig(headmatter: any, themeMeta: SlidevThemeMeta = {}, filepath?: string, verify = false) { @@ -14,6 +14,7 @@ export function resolveConfig(headmatter: any, themeMeta: SlidevThemeMeta = {}, remoteAssets: false, monaco: 'dev', download: false, + export: {} as ResolvedExportOptions, info: false, highlighter: themeHightlighter || 'prism', lineNumbers: false, diff --git a/packages/slidev/node/build.ts b/packages/slidev/node/build.ts index 8259189899..697c2a6794 100644 --- a/packages/slidev/node/build.ts +++ b/packages/slidev/node/build.ts @@ -7,6 +7,7 @@ import { resolveConfig, build as viteBuild } from 'vite' import connect from 'connect' import sirv from 'sirv' import { blue, yellow } from 'kolorist' +import type { BuildArgs } from '@slidev/types' import { ViteSlidevPlugin } from './plugins/preset' import { getIndexHtml, mergeViteConfigs } from './common' import type { ResolvedSlidevOptions } from './options' @@ -14,6 +15,7 @@ import type { ResolvedSlidevOptions } from './options' export async function build( options: ResolvedSlidevOptions, viteConfig: InlineConfig = {}, + args: BuildArgs, ) { const indexPath = resolve(options.userRoot, 'index.html') const rawConfig = await resolveConfig({}, 'build', options.entry) @@ -95,7 +97,6 @@ export async function build( } const outDir = resolve(options.userRoot, config.build.outDir) - const outFilename = options.data.config.exportFilename || 'slidev-exported.pdf' // copy index.html to 404.html for GitHub Pages await fs.copyFile(resolve(outDir, 'index.html'), resolve(outDir, '404.html')) @@ -105,7 +106,7 @@ export async function build( await fs.writeFile(redirectsPath, `${config.base}* ${config.base}index.html 200\n`, 'utf-8') if ([true, 'true', 'auto'].includes(options.data.config.download)) { - const { exportSlides } = await import('./export') + const { exportSlides, getExportOptions } = await import('./export') const port = 12445 const app = connect() @@ -121,15 +122,8 @@ export async function build( server.listen(port) await exportSlides({ port, - slides: options.data.slides, - total: options.data.slides.length, - format: 'pdf', - output: join(outDir, outFilename), base: config.base, - dark: options.data.config.colorSchema === 'dark', - width: options.data.config.canvasWidth, - height: Math.round(options.data.config.canvasWidth / options.data.config.aspectRatio), - routerMode: options.data.config.routerMode, + ...getExportOptions(args, options, outDir, 'slidev-exported.pdf'), }) server.close() } diff --git a/packages/slidev/node/cli.ts b/packages/slidev/node/cli.ts index e0c00a66d1..9c9ed3be85 100644 --- a/packages/slidev/node/cli.ts +++ b/packages/slidev/node/cli.ts @@ -201,7 +201,7 @@ cli.command( cli.command( 'build [entry]', 'Build hostable SPA', - args => commonOptions(args) + args => exportOptions(commonOptions(args)) .option('watch', { alias: 'w', default: false, @@ -229,7 +229,8 @@ cli.command( }) .strict() .help(), - async ({ entry, theme, watch, base, download, out, inspect }) => { + async (args) => { + const { entry, theme, watch, base, download, out, inspect } = args const { build } = await import('./build') const options = await resolveOptions({ entry, theme, inspect }, 'build') @@ -243,7 +244,7 @@ cli.command( watch: watch ? {} : undefined, outDir: out, }, - }) + }, args) }, ) @@ -314,65 +315,15 @@ cli.command( cli.command( 'export [entry]', 'Export slides to PDF', - args => commonOptions(args) - .option('output', { - type: 'string', - describe: 'path to the output', - }) - .option('format', { - default: 'pdf', - type: 'string', - choices: ['pdf', 'png', 'md'], - describe: 'output format', - }) - .option('timeout', { - default: 30000, - type: 'number', - describe: 'timeout for rendering the print page', - }) - .option('range', { - type: 'string', - describe: 'page ranges to export, for example "1,4-5,6"', - }) - .option('dark', { - default: false, - type: 'boolean', - describe: 'export as dark theme', - }) - .option('with-clicks', { - alias: 'c', - default: false, - type: 'boolean', - describe: 'export pages for every clicks', - }) - .option('executable-path', { - type: 'string', - describe: 'executable to override playwright bundled browser', - }) - .option('with-toc', { - default: false, - type: 'boolean', - describe: 'export pages with outline', - }) + args => exportOptions(commonOptions(args)) .strict() .help(), - async ({ - entry, - theme, - output, - format, - timeout, - range, - dark, - 'with-clicks': withClicks, - 'executable-path': executablePath, - 'with-toc': withTOC, - }) => { + async (args) => { + const { entry, theme } = args process.env.NODE_ENV = 'production' - const { exportSlides } = await import('./export') + const { exportSlides, getExportOptions } = await import('./export') const port = await findFreePort(12445) const options = await resolveOptions({ entry, theme }, 'export') - output = output || options.data.config.exportFilename || `${path.basename(entry, '.md')}-export` const server = await createServer( options, { @@ -383,23 +334,9 @@ cli.command( await server.listen(port) printInfo(options) parser.filterDisabled(options.data) - const width = options.data.config.canvasWidth - const height = Math.round(width / options.data.config.aspectRatio) - output = await exportSlides({ + const output = await exportSlides({ port, - slides: options.data.slides, - total: options.data.slides.length, - range, - format: format as any, - output, - timeout, - dark, - routerMode: options.data.config.routerMode, - width, - height, - withClicks, - executablePath, - withTOC, + ...getExportOptions(args, options), }) console.log(`${green(' ✓ ')}${dim('exported to ')}./${output}\n`) server.close() @@ -483,6 +420,44 @@ function commonOptions(args: Argv<{}>) { }) } +function exportOptions(args: Argv) { + return args + .option('output', { + type: 'string', + describe: 'path to the output', + }) + .option('format', { + type: 'string', + choices: ['pdf', 'png', 'md'], + describe: 'output format', + }) + .option('timeout', { + type: 'number', + describe: 'timeout for rendering the print page', + }) + .option('range', { + type: 'string', + describe: 'page ranges to export, for example "1,4-5,6"', + }) + .option('dark', { + type: 'boolean', + describe: 'export as dark theme', + }) + .option('with-clicks', { + alias: 'c', + type: 'boolean', + describe: 'export pages for every clicks', + }) + .option('executable-path', { + type: 'string', + describe: 'executable to override playwright bundled browser', + }) + .option('with-toc', { + type: 'boolean', + describe: 'export pages with outline', + }) +} + function printInfo(options: ResolvedSlidevOptions, port?: number, remote?: string) { console.log() console.log() diff --git a/packages/slidev/node/export.ts b/packages/slidev/node/export.ts index 65eb8233d6..ee5589d04d 100644 --- a/packages/slidev/node/export.ts +++ b/packages/slidev/node/export.ts @@ -3,10 +3,11 @@ import fs from 'fs-extra' import { blue, cyan, dim, green, yellow } from 'kolorist' import { Presets, SingleBar } from 'cli-progress' import { parseRangeString } from '@slidev/parser/core' -import type { SlideInfo, TocItem } from '@slidev/types' +import type { ExportArgs, SlideInfo, TocItem } from '@slidev/types' import { outlinePdfFactory } from '@lillallol/outline-pdf' import * as pdfLib from 'pdf-lib' import { PDFDocument } from 'pdf-lib' +import type { ResolvedSlidevOptions } from './options' import { packageExists } from './utils' export interface ExportOptions { @@ -24,7 +25,7 @@ export interface ExportOptions { height?: number withClicks?: boolean executablePath?: string - withTOC?: boolean + withToc?: boolean } function addToTree(tree: TocItem[], info: SlideInfo, slideIndexes: Record, level = 1) { @@ -155,7 +156,7 @@ export async function exportSlides({ height = 1080, withClicks = false, executablePath = undefined, - withTOC = false, + withToc = false, }: ExportOptions) { if (!packageExists('playwright-chromium')) throw new Error('The exporting for Slidev is powered by Playwright, please installed it via `npm i -D playwright-chromium`') @@ -249,7 +250,7 @@ export async function exportSlides({ if (titleSlide?.frontmatter?.info) pdf.setSubject(titleSlide.frontmatter.info) - if (withTOC) { + if (withToc) { const outlinePdf = outlinePdfFactory(pdfLib) const tocTree = slides.filter(slide => slide.title) @@ -321,3 +322,42 @@ export async function exportSlides({ browser.close() return output } + +export function getExportOptions(args: ExportArgs, options: ResolvedSlidevOptions, outDir?: string, outFilename?: string): Omit { + const config = { + ...options.data.config.export, + ...args, + withClicks: args['with-clicks'], + executablePath: args['executable-path'], + withToc: args['with-toc'], + } + const { + entry, + output, + format, + timeout, + range, + dark, + withClicks, + executablePath, + withToc, + } = config + outFilename = output || options.data.config.exportFilename || outFilename || `${path.basename(entry, '.md')}-export` + if (outDir) + outFilename = path.join(outDir, outFilename) + return { + output: outFilename, + slides: options.data.slides, + total: options.data.slides.length, + range, + format: (format || 'pdf') as 'pdf' | 'png' | 'md', + timeout: timeout || 30000, + dark: dark || options.data.config.colorSchema === 'dark', + routerMode: options.data.config.routerMode, + width: options.data.config.canvasWidth, + height: Math.round(options.data.config.canvasWidth / options.data.config.aspectRatio), + withClicks: withClicks || false, + executablePath, + withToc: withToc || false, + } +} diff --git a/packages/types/src/cli.ts b/packages/types/src/cli.ts new file mode 100644 index 0000000000..6960e06bab --- /dev/null +++ b/packages/types/src/cli.ts @@ -0,0 +1,23 @@ +export interface CommonArgs { + entry: string + theme?: string +} + +export interface ExportArgs extends CommonArgs { + output?: string + format?: string + timeout?: number + range?: string + dark?: boolean + 'with-clicks'?: boolean + 'executable-path'?: string + 'with-toc'?: boolean +} + +export interface BuildArgs extends ExportArgs { + watch: boolean + out: string + base?: string + download?: boolean + inspect: boolean +} diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 1ed19b8ae2..ba789f2678 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -1,3 +1,4 @@ +import type { ExportArgs } from './cli' import type { SlidevThemeConfig } from './types' export interface SlidevConfig { @@ -42,6 +43,12 @@ export interface SlidevConfig { * @default false */ download: boolean | string + /** + * Options for export + * + * @default {} + */ + export: ResolvedExportOptions /** * Show a copy button in code blocks * @@ -273,3 +280,9 @@ export interface ResolvedDrawingsOptions { presenterOnly: boolean syncAll: boolean } + +export interface ResolvedExportOptions extends Omit { + withClicks?: boolean + executablePath?: string + withToc?: boolean +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 835cffde2e..1df4c1e65c 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -2,3 +2,4 @@ export * from './types' export * from './setups' export * from './config' export * from './toc' +export * from './cli' diff --git a/test/__snapshots__/parser.test.ts.snap b/test/__snapshots__/parser.test.ts.snap index 25eadb74a8..08b41b9753 100644 --- a/test/__snapshots__/parser.test.ts.snap +++ b/test/__snapshots__/parser.test.ts.snap @@ -15,6 +15,7 @@ exports[`md parser > frontmatter.md > config 1`] = ` "presenterOnly": false, "syncAll": true, }, + "export": {}, "exportFilename": "", "favicon": "https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png", "fonts": { @@ -250,6 +251,7 @@ exports[`md parser > minimal.md > config 1`] = ` "presenterOnly": false, "syncAll": true, }, + "export": {}, "exportFilename": "", "favicon": "https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png", "fonts": { @@ -423,6 +425,7 @@ exports[`md parser > multi-entries.md > config 1`] = ` "presenterOnly": false, "syncAll": true, }, + "export": {}, "exportFilename": "", "favicon": "https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png", "fonts": {