1
1
import type { ResolvedSlidevOptions , SeoMeta } from '@slidev/types'
2
+ import type { ResolvableLink } from 'unhead/types'
2
3
import { existsSync , readFileSync } from 'node:fs'
3
4
import { join } from 'node:path'
4
5
import { slash } from '@antfu/utils'
5
6
import { white , yellow } from 'ansis'
6
7
import { escapeHtml } from 'markdown-it/lib/common/utils.mjs'
7
-
8
8
import { createHead , transformHtmlTemplate } from 'unhead/server'
9
9
import { version } from '../../package.json'
10
10
import { getSlideTitle } from '../commands/shared'
@@ -20,17 +20,6 @@ export default async function setupIndexHtml({ mode, entry, clientRoot, userRoot
20
20
let head = ''
21
21
let body = ''
22
22
23
- const { info, author, keywords } = data . headmatter
24
- head += [
25
- `<meta name="slidev:version" content="${ version } ">` ,
26
- mode === 'dev' && `<meta charset="slidev:entry" content="${ slash ( entry ) } ">` ,
27
- `<link rel="icon" href="${ data . config . favicon } ">` ,
28
- `<title>${ getSlideTitle ( data ) } </title>` ,
29
- info && `<meta name="description" content=${ toAttrValue ( info ) } >` ,
30
- author && `<meta name="author" content=${ toAttrValue ( author ) } >` ,
31
- keywords && `<meta name="keywords" content=${ toAttrValue ( Array . isArray ( keywords ) ? keywords . join ( ', ' ) : keywords ) } >` ,
32
- ] . filter ( Boolean ) . join ( '\n' )
33
-
34
23
for ( const root of roots ) {
35
24
const path = join ( root , 'index.html' )
36
25
if ( ! existsSync ( path ) )
@@ -48,27 +37,39 @@ export default async function setupIndexHtml({ mode, entry, clientRoot, userRoot
48
37
body += `\n${ ( index . match ( / < b o d y > ( [ \s \S ] * ?) < \/ b o d y > / i) ?. [ 1 ] || '' ) . trim ( ) } `
49
38
}
50
39
51
- if ( data . features . tweet )
40
+ if ( data . features . tweet ) {
52
41
body += '\n<script async src="https://platform.twitter.com/widgets.js"></script>'
42
+ }
53
43
44
+ const webFontsLink : ResolvableLink [ ] = [ ]
54
45
if ( data . config . fonts . webfonts . length ) {
55
46
const { provider } = data . config . fonts
56
- if ( provider === 'google' )
57
- head += `\n<link rel="stylesheet" href="${ generateGoogleFontsUrl ( data . config . fonts ) } " type="text/css">`
58
- else if ( provider === 'coollabs' )
59
- head += `\n<link rel="stylesheet" href="${ generateCoollabsFontsUrl ( data . config . fonts ) } " type="text/css">`
60
- }
61
-
62
- if ( data . headmatter . lang ) {
63
- main = main . replace ( '<html lang="en">' , `<html lang="${ data . headmatter . lang } ">` )
47
+ if ( provider === 'google' ) {
48
+ webFontsLink . push ( { rel : 'stylesheet' , href : generateGoogleFontsUrl ( data . config . fonts ) , type : 'text/css' } )
49
+ }
50
+ else if ( provider === 'coollabs' ) {
51
+ webFontsLink . push ( { rel : 'stylesheet' , href : generateCoollabsFontsUrl ( data . config . fonts ) , type : 'text/css' } )
52
+ }
64
53
}
65
54
55
+ const { info, author, keywords } = data . headmatter
66
56
const seoMeta = ( data . headmatter . seoMeta ?? { } ) as SeoMeta
67
57
68
58
const unhead = createHead ( {
69
59
init : [
70
60
{
61
+ htmlAttrs : { lang : ( data . headmatter . lang as string | undefined ) ?? 'en' } ,
62
+ title : getSlideTitle ( data ) ,
63
+ link : [
64
+ { rel : 'icon' , href : data . config . favicon } ,
65
+ ...webFontsLink ,
66
+ ] ,
71
67
meta : [
68
+ { property : 'slidev:version' , content : version } ,
69
+ { charset : 'slidev:entry' , content : mode === 'dev' && slash ( entry ) } ,
70
+ { name : 'description' , content : toAttrValue ( info ) } ,
71
+ { name : 'author' , content : toAttrValue ( author ) } ,
72
+ { name : 'keywords' , content : toAttrValue ( Array . isArray ( keywords ) ? keywords . join ( ', ' ) : keywords ) } ,
72
73
{ property : 'og:title' , content : seoMeta . ogTitle } ,
73
74
{ property : 'og:description' , content : seoMeta . ogDescription } ,
74
75
{ property : 'og:image' , content : seoMeta . ogImage } ,
0 commit comments