Skip to content

Commit 593b9f8

Browse files
authored
fix: a better way to load setup files (#2380)
1 parent 32de98b commit 593b9f8

File tree

11 files changed

+102
-142
lines changed

11 files changed

+102
-142
lines changed

packages/slidev/node/cli.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,16 @@ const CONFIG_RESTART_FIELDS: (keyof SlidevConfig)[] = [
3333
'seoMeta',
3434
]
3535

36-
/**
37-
* Files that triggers a restart when added or removed
38-
*/
39-
const FILES_CREATE_RESTART = [
40-
'global-bottom.vue',
41-
'global-top.vue',
42-
'uno.config.js',
43-
'uno.config.ts',
44-
'unocss.config.js',
45-
'unocss.config.ts',
46-
]
47-
4836
const FILES_CHANGE_RESTART = [
4937
'setup/shiki.ts',
5038
'setup/katex.ts',
5139
'setup/preparser.ts',
40+
'setup/transformers.ts',
41+
'setup/unocss.ts',
42+
'setup/vite-plugins.ts',
43+
'uno.config.ts',
44+
'unocss.config.ts',
45+
'vite.config.{js,ts,mjs,mts}',
5246
]
5347

5448
setupPreparser()
@@ -122,17 +116,17 @@ cli.command(
122116
let lastRemoteUrl: string | undefined
123117

124118
let restartTimer: ReturnType<typeof setTimeout> | undefined
125-
function restartServer() {
126-
clearTimeout(restartTimer!)
119+
async function restartServer() {
120+
await server?.close()
121+
server = undefined
122+
clearTimeout(restartTimer)
127123
restartTimer = setTimeout(() => {
128124
console.log(yellow('\n restarting...\n'))
129125
initServer()
130126
}, 500)
131127
}
132128

133129
async function initServer() {
134-
if (server)
135-
await server.close()
136130
const options = await resolveOptions({ entry, remote, theme, inspect, base }, 'dev')
137131
const host = remote !== undefined ? bind : 'localhost'
138132
port = userPort || await getPort({
@@ -210,6 +204,8 @@ cli.command(
210204
publicIp = await import('public-ip').then(r => r.publicIpv4())
211205

212206
lastRemoteUrl = printInfo(options, port, base, remote, tunnelUrl, publicIp)
207+
208+
return options
213209
}
214210

215211
async function openTunnel(port: number) {
@@ -304,17 +300,18 @@ cli.command(
304300
})
305301
}
306302

307-
initServer()
303+
const { roots } = await initServer()
308304
bindShortcut()
309305

310306
// Start watcher to restart server on file changes
311307
const { watch } = await import('chokidar')
312-
const watcher = watch([
313-
...FILES_CREATE_RESTART,
314-
...FILES_CHANGE_RESTART,
315-
], {
308+
const watchGlobs = roots
309+
.filter(i => !i.includes('node_modules'))
310+
.flatMap(root => FILES_CHANGE_RESTART.map(i => path.join(root, i)))
311+
const watcher = watch(watchGlobs, {
316312
ignored: ['node_modules', '.git'],
317313
ignoreInitial: true,
314+
ignorePermissionErrors: true,
318315
})
319316
watcher.on('unlink', (file) => {
320317
console.log(yellow(`\n file ${file} removed, restarting...\n`))
@@ -325,10 +322,6 @@ cli.command(
325322
restartServer()
326323
})
327324
watcher.on('change', (file) => {
328-
if (typeof file !== 'string')
329-
return
330-
if (FILES_CREATE_RESTART.includes(file))
331-
return
332325
console.log(yellow(`\n file ${file} changed, restarting...\n`))
333326
restartServer()
334327
})
Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,23 @@
11
import type { Awaitable } from '@antfu/utils'
22
import { existsSync } from 'node:fs'
33
import { resolve } from 'node:path'
4-
import { deepMergeWithArray } from '@antfu/utils'
54
import { loadModule } from '../utils'
65

76
export async function loadSetups<F extends (...args: any) => any>(
87
roots: string[],
98
filename: string,
109
args: Parameters<F>,
11-
extraLoader?: (root: string) => Awaitable<Awaited<ReturnType<F>>[]>,
10+
extraLoader?: (root: string) => ReturnType<F>[],
1211
) {
13-
const returns: Awaited<ReturnType<F>>[] = []
14-
for (const root of roots) {
12+
return await Promise.all(roots.flatMap((root) => {
13+
const tasks: Awaitable<ReturnType<F>>[] = []
1514
const path = resolve(root, 'setup', filename)
1615
if (existsSync(path)) {
17-
const { default: setup } = await loadModule(path) as { default: F }
18-
const ret = await setup(...args)
19-
if (ret)
20-
returns.push(ret)
16+
tasks.push(loadModule<{ default: F }>(path).then(mod => mod.default(...args)))
2117
}
22-
if (extraLoader)
23-
returns.push(...await extraLoader(root))
24-
}
25-
return returns
26-
}
27-
28-
export function mergeOptions<T, S extends Partial<T> = T>(
29-
base: T,
30-
options: S[],
31-
merger: (base: T, options: S) => T = deepMergeWithArray as any,
32-
): T {
33-
return options.reduce((acc, cur) => merger(acc, cur), base)
18+
if (extraLoader) {
19+
tasks.push(...extraLoader(root))
20+
}
21+
return tasks
22+
}))
3423
}

packages/slidev/node/setups/unocss.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,18 @@ import { loadModule } from '../utils'
1111
export default async function setupUnocss(
1212
{ clientRoot, roots, data, utils }: ResolvedSlidevOptions,
1313
) {
14-
async function loadFileConfigs(root: string): Promise<UserConfig<Theme>[]> {
15-
return (await Promise
16-
.all([
17-
resolve(root, 'uno.config.ts'),
18-
resolve(root, 'unocss.config.ts'),
19-
]
20-
.map(async (i) => {
21-
if (!existsSync(i))
22-
return undefined
23-
const loaded = await loadModule(i) as UserConfig<Theme> | { default: UserConfig<Theme> }
24-
return 'default' in loaded ? loaded.default : loaded
25-
})))
26-
.filter(x => !!x)
14+
function loadFileConfigs(root: string) {
15+
return [
16+
resolve(root, 'uno.config.ts'),
17+
resolve(root, 'unocss.config.ts'),
18+
].map(async (i) => {
19+
if (!existsSync(i))
20+
return undefined
21+
const loaded = await loadModule(i) as UserConfig | { default: UserConfig }
22+
return 'default' in loaded ? loaded.default : loaded
23+
})
2724
}
2825

29-
const tokens: string[] = await loadModule(resolve(clientRoot, '.generated/unocss-tokens.ts'))
30-
3126
const configs = [
3227
{
3328
presets: [
@@ -40,9 +35,9 @@ export default async function setupUnocss(
4035
},
4136
}),
4237
],
43-
safelist: tokens,
38+
safelist: await loadModule(resolve(clientRoot, '.generated/unocss-tokens.ts')),
4439
},
45-
...await loadFileConfigs(clientRoot),
40+
(await loadModule<{ default: UserConfig }>(resolve(clientRoot, 'uno.config.ts'))).default,
4641
...await loadSetups<UnoSetup>(roots, 'unocss.ts', [], loadFileConfigs),
4742
].filter(Boolean) as UserConfig<Theme>[]
4843

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { ResolvedSlidevOptions, VitePluginsSetup } from '@slidev/types'
2+
import type { PluginOption } from 'vite'
3+
import { loadSetups } from './load'
4+
5+
export default async function setupVitePlugins(options: ResolvedSlidevOptions): Promise<PluginOption> {
6+
const plugins = await loadSetups<VitePluginsSetup>(options.roots, 'vite-plugins.ts', [options])
7+
return plugins
8+
}

packages/slidev/node/utils.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ResolvedFontOptions, SourceSlideInfo } from '@slidev/types'
22
import type MarkdownIt from 'markdown-it'
3-
import type { Connect } from 'vite'
3+
import type { Connect, GeneralImportGlobOptions } from 'vite'
4+
import { relative } from 'node:path'
45
import { fileURLToPath } from 'node:url'
56
import { createJiti } from 'jiti'
67
import YAML from 'yaml'
@@ -97,3 +98,19 @@ export function getBodyJson(req: Connect.IncomingMessage) {
9798
})
9899
})
99100
}
101+
102+
export function makeAbsoluteImportGlob(
103+
userRoot: string,
104+
globs: string[],
105+
options: Partial<GeneralImportGlobOptions> = {},
106+
) {
107+
// Vite's import.meta.glob only supports relative paths
108+
const relativeGlobs = globs.map(glob => `./${relative(userRoot, glob)}`)
109+
const opts: GeneralImportGlobOptions = {
110+
eager: true,
111+
exhaustive: true,
112+
base: '/',
113+
...options,
114+
}
115+
return `import.meta.glob(${JSON.stringify(relativeGlobs)}, ${JSON.stringify(opts)})`
116+
}
Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,28 @@
11
import type { VirtualModuleTemplate } from './types'
2-
import { existsSync } from 'node:fs'
32
import { join } from 'node:path'
4-
import { toAtFS } from '../resolver'
3+
import { makeAbsoluteImportGlob } from '../utils'
54

65
export const templateGlobalLayers: VirtualModuleTemplate = {
76
id: `/@slidev/global-layers`,
8-
getContent({ roots }) {
9-
const imports: string[] = []
10-
11-
let n = 0
12-
function getComponent(names: string[]) {
13-
const components = roots
14-
.flatMap(root => names.map(name => join(root, name)))
15-
.filter(i => existsSync(i))
16-
17-
imports.push(components.map((path, i) => `import __n${n}_${i} from '${toAtFS(path)}'`).join('\n'))
18-
const render = components.map((_, i) => `h(__n${n}_${i})`).join(',')
19-
20-
n++
21-
22-
return `{ render: () => [${render}] }`
7+
getContent({ userRoot, roots }) {
8+
function* getComponent(name: string, names: string[]) {
9+
yield `const ${name}Components = [\n`
10+
for (const root of roots) {
11+
const globs = names.map(name => join(root, `${name}.{ts,js,vue}`))
12+
yield ' Object.values('
13+
yield makeAbsoluteImportGlob(userRoot, globs, { import: 'default' })
14+
yield ')[0],\n'
15+
}
16+
yield `].filter(Boolean)\n`
17+
yield `export const ${name} = { render: () => ${name}Components.map(comp => h(comp)) }\n\n`
2318
}
2419

25-
const globalTop = getComponent(['global.vue', 'global-top.vue', 'GlobalTop.vue'])
26-
const globalBottom = getComponent(['global-bottom.vue', 'GlobalBottom.vue'])
27-
const slideTop = getComponent(['slide-top.vue', 'SlideTop.vue'])
28-
const slideBottom = getComponent(['slide-bottom.vue', 'SlideBottom.vue'])
29-
3020
return [
31-
imports.join('\n'),
32-
`import { h } from 'vue'`,
33-
`export const GlobalTop = ${globalTop}`,
34-
`export const GlobalBottom = ${globalBottom}`,
35-
`export const SlideTop = ${slideTop}`,
36-
`export const SlideBottom = ${slideBottom}`,
37-
].join('\n')
21+
`import { h } from 'vue'\n\n`,
22+
...getComponent('GlobalTop', ['global', 'global-top', 'GlobalTop']),
23+
...getComponent('GlobalBottom', ['global-bottom', 'GlobalBottom']),
24+
...getComponent('SlideTop', ['slide-top', 'SlideTop']),
25+
...getComponent('SlideBottom', ['slide-bottom', 'SlideBottom']),
26+
].join('')
3827
},
3928
}

packages/slidev/node/virtual/setups.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import type { VirtualModuleTemplate } from './types'
2-
import { resolveSourceFiles, toAtFS } from '../resolver'
2+
import { join } from 'node:path'
3+
import { makeAbsoluteImportGlob } from '../utils'
34

45
function createSetupTemplate(name: string): VirtualModuleTemplate {
56
return {
67
id: `/@slidev/setups/${name}`,
7-
getContent({ roots }) {
8-
const setups = resolveSourceFiles(roots, `setup/${name}`)
9-
10-
const imports: string[] = []
11-
12-
setups.forEach((path, idx) => {
13-
imports.push(`import __n${idx} from '${toAtFS(path)}'`)
8+
getContent({ userRoot, roots }) {
9+
const globs = roots.map((root) => {
10+
const glob = join(root, `setup/${name}.{ts,js,mts,mjs}`)
11+
return `Object.values(${makeAbsoluteImportGlob(userRoot, [glob], { import: 'default' })})[0]`
1412
})
15-
16-
imports.push(`export default [${setups.map((_, idx) => `__n${idx}`).join(',')}]`)
17-
18-
return imports.join('\n')
13+
return `export default [${globs.join(', ')}].filter(Boolean)`
1914
},
2015
}
2116
}

packages/slidev/node/virtual/styles.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import type { VirtualModuleTemplate } from './types'
2-
import { existsSync } from 'node:fs'
3-
42
import { join } from 'node:path'
53
import { resolveImportUrl, toAtFS } from '../resolver'
4+
import { makeAbsoluteImportGlob } from '../utils'
65

76
export const templateStyle: VirtualModuleTemplate = {
87
id: '/@slidev/styles',
9-
async getContent({ data, clientRoot, roots }) {
8+
async getContent({ data, clientRoot, userRoot, roots }) {
109
function resolveUrlOfClient(name: string) {
1110
return toAtFS(join(clientRoot, name))
1211
}
@@ -20,20 +19,11 @@ export const templateStyle: VirtualModuleTemplate = {
2019
]
2120

2221
for (const root of roots) {
23-
const styles = [
24-
join(root, 'styles', 'index.ts'),
25-
join(root, 'styles', 'index.js'),
26-
join(root, 'styles', 'index.css'),
27-
join(root, 'styles.css'),
28-
join(root, 'style.css'),
29-
]
30-
31-
for (const style of styles) {
32-
if (existsSync(style)) {
33-
imports.push(`import "${toAtFS(style)}"`)
34-
continue
35-
}
36-
}
22+
imports.push(makeAbsoluteImportGlob(userRoot, [
23+
join(root, 'styles/index.{ts,js,css}'),
24+
join(root, 'styles.{ts,js,css}'),
25+
join(root, 'style.{ts,js,css}'),
26+
]))
3727
}
3828

3929
if (data.features.katex)

packages/slidev/node/vite/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ResolvedSlidevOptions, SlidevPluginOptions, SlidevServerOptions } from '@slidev/types'
22
import type { PluginOption } from 'vite'
3+
import setupVitePlugins from '../setups/vite-plugins'
34
import { createVueCompilerFlagsPlugin } from './compilerFlagsVue'
45
import { createComponentsPlugin } from './components'
56
import { createContextInjectionPlugin } from './contextInjection'
@@ -17,7 +18,6 @@ import { createRemoteAssetsPlugin } from './remoteAssets'
1718
import { createServerRefPlugin } from './serverRef'
1819
import { createStaticCopyPlugin } from './staticCopy'
1920
import { createUnocssPlugin } from './unocss'
20-
import { createUserVitePlugins } from './userPlugins'
2121
import { createVuePlugin } from './vue'
2222

2323
export function ViteSlidevPlugin(
@@ -43,7 +43,8 @@ export function ViteSlidevPlugin(
4343
createUnocssPlugin(options, pluginOptions),
4444
createStaticCopyPlugin(options, pluginOptions),
4545
createInspectPlugin(options, pluginOptions),
46-
createUserVitePlugins(options),
4746
createPatchMonacoSourceMapPlugin(),
47+
48+
setupVitePlugins(options),
4849
])
4950
}

packages/slidev/node/vite/userPlugins.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)