Skip to content

Commit 3a6b476

Browse files
committed
feat: improve hmr
1 parent 08434b8 commit 3a6b476

File tree

2 files changed

+64
-62
lines changed

2 files changed

+64
-62
lines changed

packages/client/internals/SlideContainer.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ const style = computed(() => ({
7070
<template>
7171
<div id="slide-container" ref="root">
7272
<div id="slide-content" :style="style">
73-
<component
74-
:is="route.component"
75-
:class="route.meta?.class"
76-
/>
73+
<component :is="route.component"/>
7774
</div>
7875
<slot name="controls" />
7976
</div>

packages/slidev/node/plugins/loaders.ts

Lines changed: 63 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as parser from '../parser'
77
import { ResolvedSlidevOptions } from './options'
88

99
const regexId = /^\/\@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/
10-
const regexIdQuery = /id=(\d+?)\.(md|json)$/
10+
const regexIdQuery = /(\d+?)\.(md|json)$/
1111

1212
export function getBodyJson(req: Connect.IncomingMessage) {
1313
return new Promise<any>((resolve, reject) => {
@@ -34,15 +34,16 @@ export function sendHmrReload(server: ViteDevServer, modules: ModuleNode[]) {
3434
type: 'update',
3535
updates: modules.map<Update>(m => ({
3636
acceptedPath: m.id || m.file!,
37-
path: m.id || m.file!,
37+
path: m.file!,
3838
timestamp,
3939
type: 'js-update',
4040
})),
4141
})
4242
}
4343

4444
export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoot }: ResolvedSlidevOptions): Plugin[] {
45-
let skipNext = false
45+
const slidePrefix = '/@slidev/slides/'
46+
const hmrNextModuleIds: string[] = []
4647

4748
return [
4849
{
@@ -65,19 +66,13 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
6566
if (type === 'json' && req.method === 'POST') {
6667
const body = await getBodyJson(req)
6768
Object.assign(data.slides[idx], body)
68-
skipNext = true
69-
await parser.save(data, entry)
70-
71-
sendHmrReload(
72-
server,
73-
[
74-
`${entry}?id=${idx}.md`,
75-
`${entry}?id=${idx}.json`,
76-
]
77-
.map(id => server.moduleGraph.getModuleById(id))
78-
.filter(notNullish),
69+
hmrNextModuleIds.push(
70+
`${slidePrefix}${idx}.md`,
71+
`${slidePrefix}${idx}.json`,
7972
)
8073

74+
await parser.save(data, entry)
75+
8176
res.statusCode = 200
8277
return res.end()
8378
}
@@ -87,36 +82,54 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
8782
},
8883

8984
async handleHotUpdate(ctx) {
90-
if (ctx.file === entry) {
91-
if (skipNext) {
92-
skipNext = false
93-
return
94-
}
95-
const newData = await parser.load(entry)
96-
97-
if (data.config.theme !== newData.config.theme)
98-
console.log('Theme changed')
99-
// TODO: restart the server
100-
101-
const moduleEntries = [
102-
data.slides.length !== newData.slides.length && '/@slidev/routes',
103-
JSON.stringify(data.config) !== JSON.stringify(newData.config) && '/@slidev/configs',
104-
...data.slides.map((i, idx) => `${entry}?id=${idx}.md`),
105-
...data.slides.map((i, idx) => `${entry}?id=${idx}.json`),
106-
]
107-
.filter(isTruthy)
108-
.map(id => ctx.server.moduleGraph.getModuleById(id as string))
109-
.filter(notNullish)
110-
111-
data = newData
112-
113-
moduleEntries.map(m => ctx.server.moduleGraph.invalidateModule(m))
114-
return moduleEntries
85+
if (ctx.file !== entry)
86+
return
87+
88+
const newData = await parser.load(entry)
89+
90+
if (data.config.theme !== newData.config.theme)
91+
console.log('Theme changed')
92+
// TODO: restart the server
93+
94+
const moduleIds: (string | false)[] = [
95+
...hmrNextModuleIds,
96+
]
97+
98+
hmrNextModuleIds.length = 0
99+
100+
if (data.slides.length !== newData.slides.length)
101+
moduleIds.push('/@slidev/routes')
102+
103+
if (JSON.stringify(data.config) !== JSON.stringify(newData.config))
104+
moduleIds.push('/@slidev/configs')
105+
106+
const length = Math.max(data.slides.length, newData.slides.length)
107+
108+
for (let i = 0; i < length; i++) {
109+
const a = data.slides[i]
110+
const b = newData.slides[i]
111+
112+
if (a?.raw === b?.raw)
113+
continue
114+
115+
moduleIds.push(
116+
`${slidePrefix}${i}.md`,
117+
`${slidePrefix}${i}.json`,
118+
)
115119
}
120+
const moduleEntries = moduleIds
121+
.filter(isTruthy)
122+
.map(id => ctx.server.moduleGraph.getModuleById(id as string))
123+
.filter(notNullish)
124+
125+
data = newData
126+
127+
moduleEntries.map(m => ctx.server.moduleGraph.invalidateModule(m))
128+
return moduleEntries
116129
},
117130

118131
resolveId(id) {
119-
if (id.startsWith(entry) || id.startsWith('/@slidev/'))
132+
if (id.startsWith(slidePrefix) || id.startsWith('/@slidev/'))
120133
return id
121134
return null
122135
},
@@ -135,8 +148,8 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
135148
return `export default ${JSON.stringify(data.config)}`
136149

137150
// pages
138-
if (id.startsWith(entry)) {
139-
const remaning = id.slice(entry.length + 1)
151+
if (id.startsWith(slidePrefix)) {
152+
const remaning = id.slice(slidePrefix.length)
140153
const match = remaning.match(regexIdQuery)
141154
if (match) {
142155
const [, no, type] = match
@@ -149,11 +162,11 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
149162
},
150163
},
151164
{
152-
name: 'slidev:layout-transform',
153-
enforce: 'post',
165+
name: 'slidev:layout-transform:pre',
166+
enforce: 'pre',
154167
async transform(code, id) {
155-
if (id.startsWith(entry)) {
156-
const remaning = id.slice(entry.length + 1)
168+
if (id.startsWith(slidePrefix)) {
169+
const remaning = id.slice(slidePrefix.length)
157170
const match = remaning.match(regexIdQuery)
158171
if (!match)
159172
return
@@ -162,26 +175,18 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
162175
if (type !== 'md')
163176
return
164177

165-
const layouts = await getLayouts()
166178
const pageNo = parseInt(no)
179+
const layouts = await getLayouts()
167180
const layoutName = data.slides[pageNo].frontmatter?.layout || (pageNo === 0 ? 'cover' : 'default')
168181
if (!layouts[layoutName])
169182
throw new Error(`Unknown layout "${layoutName}"`)
170183

171-
code = code.replace('export default _sfc_main', '')
172-
code = `import __layout from "${layouts[layoutName]}"\n${code}`
173-
code = `import { h } from 'vue'\n${code}`
174-
code += `\nexport default {
175-
name: "layout-${layoutName}",
176-
render: () => h(__layout, null, () => h(_sfc_main)),
177-
__file: __layout.__file,
178-
}`
179-
184+
code = code.replace(/(<script setup.*>)/g, `$1\nimport InjectedLayout from "/@fs${layouts[layoutName]}"`)
185+
code = code.replace(/<template>([\s\S]*?)<\/template>/mg, '<template><InjectedLayout v-bind="frontmatter">$1</InjectedLayout></template>')
180186
return code
181187
}
182188
},
183189
},
184-
185190
]
186191

187192
async function getLayouts() {
@@ -238,7 +243,7 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
238243
.map((i, idx) => {
239244
if (i.frontmatter?.disabled)
240245
return undefined
241-
imports.push(`import n${no} from '${entry}?id=${idx}.md'`)
246+
imports.push(`import n${no} from '${slidePrefix}${idx}.md'`)
242247
const additions = {
243248
slide: {
244249
start: i.start,

0 commit comments

Comments
 (0)