返回 slidev
layoutWrapper.ts
根目录 / packages / slidev / node / vite / layoutWrapper.ts
1 import type { ResolvedSlidevOptions } from '@slidev/types'
2 import type { Plugin } from 'vite'
3 import { bold, gray, red, yellow } from 'ansis'
4 import { toAtFS } from '../resolver'
5 import { regexSlideSourceId, templateImportContextUtils, templateInitContext, templateInjectionMarker } from './common'
6
7 const RE_SCRIPT_SETUP_TAG = /^<script setup.*>/m
8
9 export function createLayoutWrapperPlugin(
10 { data, utils }: ResolvedSlidevOptions,
11 ): Plugin {
12 return {
13 name: 'slidev:layout-wrapper',
14 transform: {
15 filter: {
16 id: {
17 include: regexSlideSourceId,
18 },
19 },
20 async handler(code, id) {
21 const match = id.match(regexSlideSourceId)
22 if (!match)
23 return
24 const [, no, type] = match
25 if (type !== 'md')
26 return
27 const index = +no - 1
28 const layouts = await utils.getLayouts()
29 const rawLayoutName = data.slides[index]?.frontmatter?.layout ?? data.slides[0]?.frontmatter?.defaults?.layout
30 let layoutName = rawLayoutName || (index === 0 ? 'cover' : 'default')
31 if (!layouts[layoutName]) {
32 console.error(red(`\nUnknown layout "${bold(layoutName)}".${yellow(' Available layouts are:')}`)
33 + Object.keys(layouts).map((i, idx) => (idx % 3 === 0 ? '\n ' : '') + gray(i.padEnd(15, ' '))).join(' '))
34 console.error()
35 layoutName = 'default'
36 }
37
38 const setupTag = code.match(RE_SCRIPT_SETUP_TAG)
39 if (!setupTag)
40 throw new Error(`[Slidev] Internal error: <script setup> block not found in slide ${index + 1}.`)
41
42 const templatePart = code.slice(0, setupTag.index!)
43 const scriptPart = code.slice(setupTag.index!)
44
45 const bodyStart = templatePart.indexOf('<template>') + 10
46 const bodyEnd = templatePart.lastIndexOf('</template>')
47 let body = code.slice(bodyStart, bodyEnd).trim()
48 if (body.startsWith('<div>') && body.endsWith('</div>'))
49 body = body.slice(5, -6)
50
51 return [
52 templatePart.slice(0, bodyStart),
53 `<InjectedLayout v-bind="_frontmatterToProps($frontmatter,${index})">\n${body}\n</InjectedLayout>`,
54 templatePart.slice(bodyEnd),
55 scriptPart.slice(0, setupTag[0].length),
56 `import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
57 templateImportContextUtils,
58 templateInitContext,
59 '$clicksContext.setup()',
60 templateInjectionMarker,
61 scriptPart.slice(setupTag[0].length),
62 ].join('\n')
63 },
64 },
65 }
66 }
67
67 lines TYPESCRIPT