1
1
import type { ExportArgs , ResolvedSlidevOptions , SlideInfo , TocItem } from '@slidev/types'
2
2
import { Buffer } from 'node:buffer'
3
3
import fs from 'node:fs/promises'
4
- import path from 'node:path'
4
+ import path , { dirname , relative } from 'node:path'
5
5
import process from 'node:process'
6
- import { clearUndefined , slash } from '@antfu/utils'
6
+ import { clearUndefined , ensureSuffix , slash } from '@antfu/utils'
7
7
import { outlinePdfFactory } from '@lillallol/outline-pdf'
8
8
import { parseRangeString } from '@slidev/parser/core'
9
9
import { blue , cyan , dim , green , yellow } from 'ansis'
@@ -43,6 +43,7 @@ export interface ExportOptions {
43
43
interface ExportPngResult {
44
44
slideIndex : number
45
45
buffer : Buffer
46
+ filename : string
46
47
}
47
48
48
49
function addToTree ( tree : TocItem [ ] , info : SlideInfo , slideIndexes : Record < number , number > , level = 1 ) {
@@ -199,6 +200,30 @@ export async function exportSlides({
199
200
} )
200
201
const page = await context . newPage ( )
201
202
const progress = createSlidevProgress ( ! perSlide )
203
+ progress . start ( pages . length )
204
+
205
+ if ( format === 'pdf' ) {
206
+ await genPagePdf ( )
207
+ }
208
+ else if ( format === 'png' ) {
209
+ await genPagePng ( output )
210
+ }
211
+ else if ( format === 'md' ) {
212
+ await genPageMd ( )
213
+ }
214
+ else if ( format === 'pptx' ) {
215
+ const buffers = await genPagePng ( false )
216
+ await genPagePptx ( buffers )
217
+ }
218
+ else {
219
+ throw new Error ( `[slidev] Unsupported exporting format "${ format } "` )
220
+ }
221
+
222
+ progress . stop ( )
223
+ browser . close ( )
224
+
225
+ const relativeOutput = slash ( relative ( '.' , output ) )
226
+ return relativeOutput . startsWith ( '.' ) ? relativeOutput : `./${ relativeOutput } `
202
227
203
228
async function go ( no : number | string , clicks ?: string ) {
204
229
const query = new URLSearchParams ( )
@@ -390,11 +415,9 @@ export async function exportSlides({
390
415
await fs . writeFile ( output , pdfData )
391
416
}
392
417
393
- async function genPagePngOnePiece ( writeToDisk : boolean ) {
418
+ async function genPagePngOnePiece ( writeToDisk : string | false ) {
394
419
const result : ExportPngResult [ ] = [ ]
395
420
await go ( 'print' )
396
- await fs . rm ( output , { force : true , recursive : true } )
397
- await fs . mkdir ( output , { recursive : true } )
398
421
const slideContainers = page . locator ( '.print-slide-container' )
399
422
const count = await slideContainers . count ( )
400
423
for ( let i = 0 ; i < count ; i ++ ) {
@@ -404,24 +427,26 @@ export async function exportSlides({
404
427
const buffer = await slideContainers . nth ( i ) . screenshot ( {
405
428
omitBackground,
406
429
} )
407
- result . push ( { slideIndex : slideNo - 1 , buffer } )
430
+ const filename = `${ withClicks ? id : slideNo } .png`
431
+ result . push ( { slideIndex : slideNo - 1 , buffer, filename } )
408
432
if ( writeToDisk )
409
- await fs . writeFile ( path . join ( output , ` ${ withClicks ? id : slideNo } .png` ) , buffer )
433
+ await fs . writeFile ( path . join ( writeToDisk , filename ) , buffer )
410
434
}
411
435
return result
412
436
}
413
437
414
- async function genPagePngPerSlide ( writeToDisk : boolean ) {
438
+ async function genPagePngPerSlide ( writeToDisk : string | false ) {
415
439
const result : ExportPngResult [ ] = [ ]
416
440
const genScreenshot = async ( no : number , clicks ?: string ) => {
417
441
await go ( no , clicks )
418
442
const buffer = await page . screenshot ( {
419
443
omitBackground,
420
444
} )
421
- result . push ( { slideIndex : no - 1 , buffer } )
445
+ const filename = `${ no . toString ( ) . padStart ( 2 , '0' ) } ${ clicks ? `-${ clicks } ` : '' } .png`
446
+ result . push ( { slideIndex : no - 1 , buffer, filename } )
422
447
if ( writeToDisk ) {
423
448
await fs . writeFile (
424
- path . join ( output , ` ${ no . toString ( ) . padStart ( 2 , '0' ) } ${ clicks ? `- ${ clicks } ` : '' } .png` ) ,
449
+ path . join ( writeToDisk , filename ) ,
425
450
buffer ,
426
451
)
427
452
}
@@ -439,25 +464,25 @@ export async function exportSlides({
439
464
: genPagePdfOnePiece ( )
440
465
}
441
466
442
- function genPagePng ( writeToDisk = true ) {
467
+ async function genPagePng ( writeToDisk : string | false ) {
468
+ if ( writeToDisk ) {
469
+ await fs . rm ( writeToDisk , { force : true , recursive : true } )
470
+ await fs . mkdir ( writeToDisk , { recursive : true } )
471
+ }
443
472
return perSlide
444
473
? genPagePngPerSlide ( writeToDisk )
445
474
: genPagePngOnePiece ( writeToDisk )
446
475
}
447
476
448
477
async function genPageMd ( ) {
449
- const files = await fs . readdir ( output )
450
- const mds : string [ ] = files . map ( ( file , i , files ) => {
451
- const slideIndex = getSlideIndex ( file )
452
- const mdImg = `![${ slides [ slideIndex ] ?. title } ](./${ slash ( path . join ( output , file ) ) } )\n\n`
453
- if ( ( i + 1 === files . length || getSlideIndex ( files [ i + 1 ] ) !== slideIndex ) && slides [ slideIndex ] ?. note )
454
- return `${ mdImg } ${ slides [ slideIndex ] ?. note } \n\n`
455
- return mdImg
456
- } )
457
-
458
- if ( ! output . endsWith ( '.md' ) )
459
- output = `${ output } .md`
460
- await fs . writeFile ( output , mds . join ( '' ) )
478
+ const pngs = await genPagePng ( dirname ( output ) )
479
+ const content = slides . map ( ( { title, index, note } ) =>
480
+ pngs . filter ( ( { slideIndex } ) => slideIndex === index )
481
+ . map ( ( { filename } ) => `\n\n` )
482
+ . join ( '' )
483
+ + ( note ? `${ note . trim ( ) } \n\n` : '' ) ,
484
+ ) . join ( '---\n\n' )
485
+ await fs . writeFile ( ensureSuffix ( '.md' , output ) , content )
461
486
}
462
487
463
488
// Ported from https://github.com/marp-team/marp-cli/blob/main/src/converter.ts
@@ -500,11 +525,6 @@ export async function exportSlides({
500
525
await fs . writeFile ( output , buffer )
501
526
}
502
527
503
- function getSlideIndex ( file : string ) : number {
504
- const slideId = file . substring ( 0 , file . indexOf ( '.' ) ) . split ( '-' ) [ 0 ]
505
- return Number ( slideId ) - 1
506
- }
507
-
508
528
// Adds metadata (title, author, keywords) to PDF document, mutating it
509
529
function addPdfMetadata ( pdf : PDFDocument ) : void {
510
530
const titleSlide = slides [ 0 ]
@@ -536,33 +556,9 @@ export async function exportSlides({
536
556
537
557
return await outlinePdf ( { outline, pdf } )
538
558
}
539
-
540
- progress . start ( pages . length )
541
-
542
- if ( format === 'pdf' ) {
543
- await genPagePdf ( )
544
- }
545
- else if ( format === 'png' ) {
546
- await genPagePng ( )
547
- }
548
- else if ( format === 'md' ) {
549
- await genPagePng ( )
550
- await genPageMd ( )
551
- }
552
- else if ( format === 'pptx' ) {
553
- const buffers = await genPagePng ( false )
554
- await genPagePptx ( buffers )
555
- }
556
- else {
557
- throw new Error ( `Unsupported exporting format "${ format } "` )
558
- }
559
-
560
- progress . stop ( )
561
- browser . close ( )
562
- return output
563
559
}
564
560
565
- export function getExportOptions ( args : ExportArgs , options : ResolvedSlidevOptions , outDir ?: string , outFilename ?: string ) : Omit < ExportOptions , 'port' | 'base' > {
561
+ export function getExportOptions ( args : ExportArgs , options : ResolvedSlidevOptions , outFilename ?: string ) : Omit < ExportOptions , 'port' | 'base' > {
566
562
const config = {
567
563
...options . data . config . export ,
568
564
...args ,
@@ -592,8 +588,6 @@ export function getExportOptions(args: ExportArgs, options: ResolvedSlidevOption
592
588
omitBackground,
593
589
} = config
594
590
outFilename = output || options . data . config . exportFilename || outFilename || `${ path . basename ( entry , '.md' ) } -export`
595
- if ( outDir )
596
- outFilename = path . join ( outDir , outFilename )
597
591
return {
598
592
output : outFilename ,
599
593
slides : options . data . slides ,
0 commit comments