Skip to content

Commit c35e1c9

Browse files
committed
feat: server reload on theme change
1 parent 4fbf1d8 commit c35e1c9

File tree

9 files changed

+132
-80
lines changed

9 files changed

+132
-80
lines changed

demo/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"private": true,
33
"version": "0.0.0-beta.1",
44
"scripts": {
5-
"dev": "nodemon -w '../packages/slidev/dist/*.js' --exec 'slidev'",
6-
"starter": "nodemon -w '../packages/slidev/dist/*.js' --exec 'slidev starter.md'",
5+
"dev": "nodemon -w '../packages/slidev/dist/*.js' --exec 'slidev --open=false'",
6+
"starter": "nodemon -w '../packages/slidev/dist/*.js' --exec 'slidev starter.md --open=false'",
77
"build": "slidev build",
88
"export": "slidev export"
99
},

packages/slidev/node/build.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@ import { promises as fs } from 'fs'
22
import { build as viteBuild, InlineConfig, mergeConfig } from 'vite'
33
import { ViteSlidevPlugin } from './plugins/preset'
44
import { getIndexHtml } from './common'
5-
import { resolveOptions } from './plugins/options'
5+
import { resolveOptions, SlidevEntryOptions, SlidevPluginOptions } from './plugins/options'
66

7-
export async function build(entry: string, config: InlineConfig = {}) {
8-
const options = await resolveOptions(entry)
9-
await fs.writeFile('index.html', await getIndexHtml(options), 'utf-8')
7+
export async function build(
8+
entry: SlidevEntryOptions = {},
9+
pluginOptions: Omit<SlidevPluginOptions, 'slidev'> = {},
10+
viteConfig: InlineConfig = {},
11+
) {
12+
const resolved = await resolveOptions(entry)
13+
await fs.writeFile('index.html', await getIndexHtml(resolved), 'utf-8')
1014
try {
1115
await viteBuild(
1216
mergeConfig(
13-
config,
17+
viteConfig,
1418
{
1519
plugins: [
16-
await ViteSlidevPlugin({
17-
resolved: options,
20+
ViteSlidevPlugin({
21+
...pluginOptions,
22+
slidev: resolved,
1823
}),
1924
],
2025
},

packages/slidev/node/cli.ts

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import path from 'path'
22
import fs from 'fs-extra'
33
import yargs, { Argv } from 'yargs'
44
import { prompt } from 'enquirer'
5-
import { blue } from 'kolorist'
5+
import { green, yellow } from 'kolorist'
6+
import { ViteDevServer } from 'vite'
67
import { version } from '../package.json'
78
import { build } from './build'
89
import { createServer } from './server'
@@ -15,7 +16,7 @@ function commonOptions(args: Argv<{}>) {
1516
type: 'string',
1617
describe: 'path to the slides markdown entry',
1718
})
18-
.option('template', {
19+
.option('theme', {
1920
alias: 't',
2021
type: 'string',
2122
describe: 'overide theme',
@@ -47,7 +48,7 @@ cli.command(
4748
describe: 'open in browser',
4849
})
4950
.help(),
50-
async({ entry, port, open }) => {
51+
async({ entry, theme, port, open }) => {
5152
if (!fs.existsSync(entry)) {
5253
const { create } = await prompt<{create: boolean}>({
5354
name: 'create',
@@ -60,14 +61,35 @@ cli.command(
6061
process.exit(0)
6162
}
6263

63-
const server = await createServer(entry, {
64-
server: {
65-
port,
66-
open,
67-
},
68-
})
69-
server.listen()
70-
server.watcher.add(entry)
64+
let server: ViteDevServer | undefined
65+
66+
async function initServer() {
67+
if (server)
68+
await server.close()
69+
server = (await createServer(
70+
{
71+
entry,
72+
theme,
73+
},
74+
{
75+
onDataReload(newData, data) {
76+
if (!theme && newData.config.theme !== data.config.theme) {
77+
console.log(yellow('Slidev reloaded on theme change'))
78+
initServer()
79+
}
80+
},
81+
},
82+
{
83+
server: {
84+
port,
85+
open,
86+
},
87+
},
88+
)).server
89+
await server.listen()
90+
}
91+
92+
initServer()
7193
},
7294
)
7395

@@ -76,8 +98,8 @@ cli.command(
7698
'Build hostable SPA',
7799
args => commonOptions(args)
78100
.help(),
79-
async({ entry }) => {
80-
await build(entry)
101+
async(args) => {
102+
await build(args)
81103
},
82104
)
83105

@@ -102,12 +124,25 @@ cli.command(
102124
describe: 'path to the the port output',
103125
})
104126
.help(),
105-
async({ entry, output }) => {
127+
async({ entry, theme, output }) => {
106128
output = output || `${path.basename(entry, '.md')}.pdf`
107129
process.env.NODE_ENV = 'production'
108130
const { genratePDF } = await import('./export')
109-
await genratePDF(entry, output, { logLevel: 'error' })
110-
console.log(blue(`PDF Exported: ./${output}`))
131+
const port = 12445
132+
const { server, resolved } = await createServer(
133+
{ entry, theme },
134+
{},
135+
{
136+
server: { port },
137+
logLevel: 'error',
138+
clearScreen: true,
139+
},
140+
)
141+
await server.listen()
142+
parser.filterDisabled(resolved.data)
143+
await genratePDF(port, resolved.data.slides.length, output)
144+
console.log(green(`PDF Exported: ./${output}`))
145+
server.close()
111146
process.exit(0)
112147
},
113148
)

packages/slidev/node/export.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
import { promises as fs } from 'fs'
22
import { chromium } from 'playwright'
3-
import { InlineConfig } from 'vite'
43
import { PDFDocument } from 'pdf-lib'
54
import { yellow } from 'kolorist'
6-
import { createServer } from './server'
7-
import { filterDisabled, load } from './parser'
8-
9-
export async function genratePDF(entry = 'slides.md', output = 'slides.pdf', config: InlineConfig = {}) {
10-
const { slides } = filterDisabled(await load(entry))
11-
const server = await createServer(entry, {
12-
...config,
13-
logLevel: 'silent',
14-
clearScreen: false,
15-
})
16-
const port = 18724
17-
await server.listen(port)
185

6+
export async function genratePDF(port = 18724, pages = 0, output = 'slides.pdf') {
197
const browser = await chromium.launch()
208
const context = await browser.newContext({
219
viewport: {
@@ -27,9 +15,8 @@ export async function genratePDF(entry = 'slides.md', output = 'slides.pdf', con
2715
const page = await context.newPage()
2816

2917
const buffers: Buffer[] = []
30-
const pagesCount = slides.length
31-
for (let i = 0; i < pagesCount; i++) {
32-
console.log(yellow(`Rendering page ${i + 1} / ${pagesCount}`))
18+
for (let i = 0; i < pages; i++) {
19+
console.log(`${yellow('Rendering')} page ${i + 1} / ${pages}`)
3320
await page.goto(`http://localhost:${port}/${i}?print`, {
3421
waitUntil: 'networkidle',
3522
})
@@ -62,6 +49,5 @@ export async function genratePDF(entry = 'slides.md', output = 'slides.pdf', con
6249
const buffer = await mergedPdf.save()
6350
await fs.writeFile(output, buffer)
6451
browser.close()
65-
server.close()
6652
return output
6753
}

packages/slidev/node/parser.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { promises as fs } from 'fs'
22
import matter from 'gray-matter'
33
import YAML from 'js-yaml'
44

5-
export interface SlidesMarkdownInfo {
5+
export interface SlideInfo {
66
start: number
77
end: number
88
raw: string
@@ -25,9 +25,9 @@ export interface SlidevConfig {
2525
theme: string
2626
}
2727

28-
export interface SlidesMarkdown {
28+
export interface SlidevMarkdown {
2929
filepath?: string
30-
slides: SlidesMarkdownInfo[]
30+
slides: SlideInfo[]
3131
options: ParseOptions
3232
raw: string
3333
config: SlidevConfig
@@ -42,13 +42,13 @@ export async function load(
4242
return parse(markdown, filepath, options)
4343
}
4444

45-
export async function save(data: SlidesMarkdown, filepath?: string) {
45+
export async function save(data: SlidevMarkdown, filepath?: string) {
4646
filepath = filepath || data.filepath!
4747

4848
await fs.writeFile(filepath, stringify(data), 'utf-8')
4949
}
5050

51-
export function stringify(data: SlidesMarkdown) {
51+
export function stringify(data: SlidevMarkdown) {
5252
return `${
5353
data.slides
5454
.map(stringifySlide)
@@ -57,12 +57,12 @@ export function stringify(data: SlidesMarkdown) {
5757
}\n`
5858
}
5959

60-
export function filterDisabled(data: SlidesMarkdown) {
60+
export function filterDisabled(data: SlidevMarkdown) {
6161
data.slides = data.slides.filter(i => !i.frontmatter?.disabled)
6262
return data
6363
}
6464

65-
function stringifySlide(data: SlidesMarkdownInfo, idx = 1) {
65+
function stringifySlide(data: SlideInfo, idx = 1) {
6666
if (!data.raw)
6767
prettifySlide(data)
6868

@@ -71,15 +71,15 @@ function stringifySlide(data: SlidesMarkdownInfo, idx = 1) {
7171
: `------\n${data.raw}`
7272
}
7373

74-
function prettifySlide(data: SlidesMarkdownInfo) {
74+
function prettifySlide(data: SlideInfo) {
7575
data.content = `\n${data.content.trim()}\n\n`
7676
data.raw = Object.keys(data.frontmatter || {}).length
7777
? `---\n${YAML.safeDump(data.frontmatter).trim()}\n---\n${data.content}`
7878
: data.content
7979
return data
8080
}
8181

82-
export function prettify(data: SlidesMarkdown) {
82+
export function prettify(data: SlidevMarkdown) {
8383
data.slides.forEach(prettifySlide)
8484
return data
8585
}
@@ -88,9 +88,9 @@ export function parse(
8888
markdown: string,
8989
filepath?: string,
9090
options: ParseOptions = {},
91-
): SlidesMarkdown {
91+
): SlidevMarkdown {
9292
const lines = markdown.split(/\n/g)
93-
const slides: SlidesMarkdownInfo[] = []
93+
const slides: SlideInfo[] = []
9494
let start = 0
9595
let dividers = 0
9696

packages/slidev/node/plugins/loaders.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isTruthy, notNullish, objectMap } from '@antfu/utils'
44
import type { Connect } from 'vite'
55
import fg from 'fast-glob'
66
import * as parser from '../parser'
7-
import { ResolvedSlidevOptions } from './options'
7+
import { ResolvedSlidevOptions, SlidevPluginOptions } from './options'
88

99
const regexId = /^\/\@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/
1010
const regexIdQuery = /(\d+?)\.(md|json)$/
@@ -41,7 +41,7 @@ export function sendHmrReload(server: ViteDevServer, modules: ModuleNode[]) {
4141
})
4242
}
4343

44-
export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoot }: ResolvedSlidevOptions): Plugin[] {
44+
export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoot }: ResolvedSlidevOptions, pluginOptions: SlidevPluginOptions): Plugin[] {
4545
const slidePrefix = '/@slidev/slides/'
4646
const hmrNextModuleIds: string[] = []
4747

@@ -87,10 +87,6 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
8787

8888
const newData = await parser.load(entry)
8989

90-
if (data.config.theme !== newData.config.theme)
91-
console.log('Theme changed')
92-
// TODO: restart the server
93-
9490
const moduleIds: (string | false)[] = [
9591
...hmrNextModuleIds,
9692
]
@@ -117,11 +113,14 @@ export function createSlidesLoader({ data, entry, clientRoot, themeRoot, userRoo
117113
`${slidePrefix}${i}.json`,
118114
)
119115
}
116+
120117
const moduleEntries = moduleIds
121118
.filter(isTruthy)
122119
.map(id => ctx.server.moduleGraph.getModuleById(id as string))
123120
.filter(notNullish)
124121

122+
pluginOptions.onDataReload?.(newData, data)
123+
125124
data = newData
126125

127126
moduleEntries.map(m => ctx.server.moduleGraph.invalidateModule(m))

packages/slidev/node/plugins/options.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,31 @@ import WindiCSS from 'vite-plugin-windicss'
77
import RemoteAssets from 'vite-plugin-remote-assets'
88
import { ArgumentsType } from '@antfu/utils'
99
import * as parser from '../parser'
10+
import { SlidevMarkdown } from '../parser'
1011

11-
export interface SlidevOptions {
12+
export interface SlidevEntryOptions {
1213
/**
1314
* Markdown entry
1415
*
1516
* @default 'slides.md'
1617
*/
1718
entry?: string
19+
1820
/**
19-
* Theme name
20-
*
21-
* @default `@slidev/theme-default`
21+
* Theme id
2222
*/
2323
theme?: string
24+
25+
/**
26+
* Root path
27+
*
28+
* @default process.cwd()
29+
*/
30+
userRoot?: string
2431
}
2532

2633
export interface ResolvedSlidevOptions {
27-
data: parser.SlidesMarkdown
34+
data: SlidevMarkdown
2835
entry: string
2936
userRoot: string
3037
cliRoot: string
@@ -33,14 +40,15 @@ export interface ResolvedSlidevOptions {
3340
themeRoot: string
3441
}
3542

36-
export interface SlidevPluginOptions extends SlidevOptions {
43+
export interface SlidevPluginOptions extends SlidevEntryOptions {
3744
vue?: ArgumentsType<typeof Vue>[0]
3845
markdown?: ArgumentsType<typeof Markdown>[0]
3946
components?: ArgumentsType<typeof ViteComponents>[0]
4047
windicss?: ArgumentsType<typeof WindiCSS>[0]
4148
icons?: ArgumentsType<typeof ViteIcons>[0]
4249
remoteAssets?: ArgumentsType<typeof RemoteAssets>[0]
43-
resolved?: ResolvedSlidevOptions
50+
slidev: ResolvedSlidevOptions
51+
onDataReload?: (newData: SlidevMarkdown, data: SlidevMarkdown) => void
4452
}
4553

4654
export function getClientRoot() {
@@ -55,9 +63,13 @@ export function getThemeRoot(name: string) {
5563
return dirname(require.resolve(`${name}/package.json`))
5664
}
5765

58-
export async function resolveOptions(entry = 'slides.md', userRoot = process.cwd()): Promise<ResolvedSlidevOptions> {
66+
export async function resolveOptions(options: SlidevEntryOptions): Promise<ResolvedSlidevOptions> {
67+
const {
68+
entry = 'slides.md',
69+
userRoot = process.cwd(),
70+
} = options
5971
const data = await parser.load(entry)
60-
const theme = data.config.theme
72+
const theme = options.theme || data.config.theme
6173

6274
return {
6375
data,

0 commit comments

Comments
 (0)