Skip to content

Commit

Permalink
fix: HTML head tags injection (#1578)
Browse files Browse the repository at this point in the history
  • Loading branch information
KermanX committed May 8, 2024
1 parent 9f05662 commit 0b62727
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 54 deletions.
38 changes: 35 additions & 3 deletions packages/slidev/node/commands/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,48 @@ import { existsSync, promises as fs } from 'node:fs'
import { join } from 'node:path'
import { loadConfigFromFile, mergeConfig, resolveConfig } from 'vite'
import type { ConfigEnv, InlineConfig } from 'vite'
import type { ResolvedSlidevOptions } from '@slidev/types'
import { generateGoogleFontsUrl } from '../utils'
import type { ResolvedSlidevOptions, SlidevData } from '@slidev/types'
import { isString } from 'unocss'
import MarkdownIt from 'markdown-it'
import markdownItLink from '../syntax/markdown-it/markdown-it-link'
import { generateGoogleFontsUrl, stringifyMarkdownTokens } from '../utils'
import { toAtFS } from '../resolver'

export const sharedMd = MarkdownIt({ html: true })
sharedMd.use(markdownItLink)

export function getTitle(data: SlidevData) {
if (isString(data.config.title)) {
const tokens = sharedMd.parseInline(data.config.title, {})
return stringifyMarkdownTokens(tokens)
}
return data.config.title
}

function escapeHtml(unsafe: unknown) {
return JSON.stringify(
String(unsafe)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;'),
)
}

export async function getIndexHtml({ clientRoot, roots, data }: ResolvedSlidevOptions): Promise<string> {
let main = await fs.readFile(join(clientRoot, 'index.html'), 'utf-8')
let head = ''
let body = ''

head += `<link rel="icon" href="${data.config.favicon}">`
const { info, author, keywords } = data.headmatter
head += [
`<link rel="icon" href="${data.config.favicon}">`,
`<title>${getTitle(data)}</title>`,
info && `<meta name="description" content=${escapeHtml(info)}>`,
author && `<meta name="author" content=${escapeHtml(author)}>`,
keywords && `<meta name="keywords" content=${escapeHtml(Array.isArray(keywords) ? keywords.join(', ') : keywords)}>`,
].filter(Boolean).join('\n')

for (const root of roots) {
const path = join(root, 'index.html')
Expand Down
57 changes: 6 additions & 51 deletions packages/slidev/node/vite/loaders.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import path from 'node:path'
import type { Connect, HtmlTagDescriptor, ModuleNode, Plugin, Update, ViteDevServer } from 'vite'
import { isString, isTruthy, notNullish, range } from '@antfu/utils'
import type { Connect, ModuleNode, Plugin, Update, ViteDevServer } from 'vite'
import { notNullish, range } from '@antfu/utils'
import fg from 'fast-glob'
import Markdown from 'markdown-it'
import { bold, gray, red, yellow } from 'kolorist'

import type { ResolvedSlidevOptions, SlideInfo, SlidePatch, SlidevPluginOptions, SlidevServerOptions } from '@slidev/types'
import * as parser from '@slidev/parser/fs'
import equal from 'fast-deep-equal'

import type { LoadResult } from 'rollup'
import { stringifyMarkdownTokens, updateFrontmatterPatch } from '../utils'
import { updateFrontmatterPatch } from '../utils'
import { toAtFS } from '../resolver'
import { templates } from '../virtual'
import type { VirtualModuleTempalteContext } from '../virtual/types'
Expand All @@ -19,7 +18,7 @@ import { VIRTUAL_SLIDE_PREFIX, templateSlides } from '../virtual/slides'
import { templateConfigs } from '../virtual/configs'
import { templateMonacoRunDeps } from '../virtual/monaco-deps'
import { templateMonacoTypes } from '../virtual/monaco-types'
import markdownItLink from '../syntax/markdown-it/markdown-it-link'
import { sharedMd } from '../commands/shared'

const regexId = /^\/\@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/
const regexIdQuery = /(\d+?)\.(md|json|frontmatter)$/
Expand Down Expand Up @@ -64,12 +63,9 @@ export function sendHmrReload(server: ViteDevServer, modules: ModuleNode[]) {
})
}

const md = Markdown({ html: true })
md.use(markdownItLink)

function renderNote(text: string = '') {
let clickCount = 0
const html = md.render(text
const html = sharedMd.render(text
// replace [click] marker with span
.replace(/\[click(?::(\d+))?\]/gi, (_, count = 1) => {
clickCount += Number(count)
Expand Down Expand Up @@ -103,7 +99,7 @@ export function createSlidesLoader(
const { data, clientRoot, roots, mode } = options

const templateCtx: VirtualModuleTempalteContext = {
md,
md: sharedMd,
async getLayouts() {
const now = Date.now()
if (now - _layouts_cache_time < 2000)
Expand Down Expand Up @@ -422,39 +418,6 @@ export function createSlidesLoader(
return replaced
},
},
{
name: 'slidev:index-html-transform',
transformIndexHtml() {
const { info, author, keywords } = data.headmatter
return [
{
tag: 'title',
children: getTitle(),
},
info && {
tag: 'meta',
attrs: {
name: 'description',
content: info,
},
},
author && {
tag: 'meta',
attrs: {
name: 'author',
content: author,
},
},
keywords && {
tag: 'meta',
attrs: {
name: 'keywords',
content: Array.isArray(keywords) ? keywords.join(', ') : keywords,
},
},
].filter(isTruthy) as HtmlTagDescriptor[]
},
},
]

function updateServerWatcher() {
Expand Down Expand Up @@ -554,12 +517,4 @@ export function createSlidesLoader(
// no setup script and not a vue component
return `<script setup>\n${imports.join('\n')}\n</script>\n${code}`
}

function getTitle() {
if (isString(data.config.title)) {
const tokens = md.parseInline(data.config.title, {})
return stringifyMarkdownTokens(tokens)
}
return data.config.title
}
}

0 comments on commit 0b62727

Please sign in to comment.