Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 18 additions & 25 deletions packages/slidev/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,16 @@ const CONFIG_RESTART_FIELDS: (keyof SlidevConfig)[] = [
'seoMeta',
]

/**
* Files that triggers a restart when added or removed
*/
const FILES_CREATE_RESTART = [
'global-bottom.vue',
'global-top.vue',
'uno.config.js',
'uno.config.ts',
'unocss.config.js',
'unocss.config.ts',
]

const FILES_CHANGE_RESTART = [
'setup/shiki.ts',
'setup/katex.ts',
'setup/preparser.ts',
'setup/transformers.ts',
'setup/unocss.ts',
'setup/vite-plugins.ts',
'uno.config.ts',
'unocss.config.ts',
'vite.config.{js,ts,mjs,mts}',
]

setupPreparser()
Expand Down Expand Up @@ -122,17 +116,17 @@ cli.command(
let lastRemoteUrl: string | undefined

let restartTimer: ReturnType<typeof setTimeout> | undefined
function restartServer() {
clearTimeout(restartTimer!)
async function restartServer() {
await server?.close()
server = undefined
clearTimeout(restartTimer)
restartTimer = setTimeout(() => {
console.log(yellow('\n restarting...\n'))
initServer()
}, 500)
}

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

lastRemoteUrl = printInfo(options, port, base, remote, tunnelUrl, publicIp)

return options
}

async function openTunnel(port: number) {
Expand Down Expand Up @@ -304,17 +300,18 @@ cli.command(
})
}

initServer()
const { roots } = await initServer()
bindShortcut()

// Start watcher to restart server on file changes
const { watch } = await import('chokidar')
const watcher = watch([
...FILES_CREATE_RESTART,
...FILES_CHANGE_RESTART,
], {
const watchGlobs = roots
.filter(i => !i.includes('node_modules'))
.flatMap(root => FILES_CHANGE_RESTART.map(i => path.join(root, i)))
const watcher = watch(watchGlobs, {
ignored: ['node_modules', '.git'],
ignoreInitial: true,
ignorePermissionErrors: true,
})
watcher.on('unlink', (file) => {
console.log(yellow(`\n file ${file} removed, restarting...\n`))
Expand All @@ -325,10 +322,6 @@ cli.command(
restartServer()
})
watcher.on('change', (file) => {
if (typeof file !== 'string')
return
if (FILES_CREATE_RESTART.includes(file))
return
console.log(yellow(`\n file ${file} changed, restarting...\n`))
restartServer()
})
Expand Down
29 changes: 9 additions & 20 deletions packages/slidev/node/setups/load.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,23 @@
import type { Awaitable } from '@antfu/utils'
import { existsSync } from 'node:fs'
import { resolve } from 'node:path'
import { deepMergeWithArray } from '@antfu/utils'
import { loadModule } from '../utils'

export async function loadSetups<F extends (...args: any) => any>(
roots: string[],
filename: string,
args: Parameters<F>,
extraLoader?: (root: string) => Awaitable<Awaited<ReturnType<F>>[]>,
extraLoader?: (root: string) => ReturnType<F>[],
) {
const returns: Awaited<ReturnType<F>>[] = []
for (const root of roots) {
return await Promise.all(roots.flatMap((root) => {
const tasks: Awaitable<ReturnType<F>>[] = []
const path = resolve(root, 'setup', filename)
if (existsSync(path)) {
const { default: setup } = await loadModule(path) as { default: F }
const ret = await setup(...args)
if (ret)
returns.push(ret)
tasks.push(loadModule<{ default: F }>(path).then(mod => mod.default(...args)))
}
if (extraLoader)
returns.push(...await extraLoader(root))
}
return returns
}

export function mergeOptions<T, S extends Partial<T> = T>(
base: T,
options: S[],
merger: (base: T, options: S) => T = deepMergeWithArray as any,
): T {
return options.reduce((acc, cur) => merger(acc, cur), base)
if (extraLoader) {
tasks.push(...extraLoader(root))
}
return tasks
}))
}
29 changes: 12 additions & 17 deletions packages/slidev/node/setups/unocss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,18 @@ import { loadModule } from '../utils'
export default async function setupUnocss(
{ clientRoot, roots, data, utils }: ResolvedSlidevOptions,
) {
async function loadFileConfigs(root: string): Promise<UserConfig<Theme>[]> {
return (await Promise
.all([
resolve(root, 'uno.config.ts'),
resolve(root, 'unocss.config.ts'),
]
.map(async (i) => {
if (!existsSync(i))
return undefined
const loaded = await loadModule(i) as UserConfig<Theme> | { default: UserConfig<Theme> }
return 'default' in loaded ? loaded.default : loaded
})))
.filter(x => !!x)
function loadFileConfigs(root: string) {
return [
resolve(root, 'uno.config.ts'),
resolve(root, 'unocss.config.ts'),
].map(async (i) => {
if (!existsSync(i))
return undefined
const loaded = await loadModule(i) as UserConfig | { default: UserConfig }
return 'default' in loaded ? loaded.default : loaded
})
}

const tokens: string[] = await loadModule(resolve(clientRoot, '.generated/unocss-tokens.ts'))

const configs = [
{
presets: [
Expand All @@ -40,9 +35,9 @@ export default async function setupUnocss(
},
}),
],
safelist: tokens,
safelist: await loadModule(resolve(clientRoot, '.generated/unocss-tokens.ts')),
},
...await loadFileConfigs(clientRoot),
(await loadModule<{ default: UserConfig }>(resolve(clientRoot, 'uno.config.ts'))).default,
...await loadSetups<UnoSetup>(roots, 'unocss.ts', [], loadFileConfigs),
].filter(Boolean) as UserConfig<Theme>[]

Expand Down
8 changes: 8 additions & 0 deletions packages/slidev/node/setups/vite-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ResolvedSlidevOptions, VitePluginsSetup } from '@slidev/types'
import type { PluginOption } from 'vite'
import { loadSetups } from './load'

export default async function setupVitePlugins(options: ResolvedSlidevOptions): Promise<PluginOption> {
const plugins = await loadSetups<VitePluginsSetup>(options.roots, 'vite-plugins.ts', [options])
return plugins
}
19 changes: 18 additions & 1 deletion packages/slidev/node/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ResolvedFontOptions, SourceSlideInfo } from '@slidev/types'
import type MarkdownIt from 'markdown-it'
import type { Connect } from 'vite'
import type { Connect, GeneralImportGlobOptions } from 'vite'
import { relative } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createJiti } from 'jiti'
import YAML from 'yaml'
Expand Down Expand Up @@ -97,3 +98,19 @@ export function getBodyJson(req: Connect.IncomingMessage) {
})
})
}

export function makeAbsoluteImportGlob(
userRoot: string,
globs: string[],
options: Partial<GeneralImportGlobOptions> = {},
) {
// Vite's import.meta.glob only supports relative paths
const relativeGlobs = globs.map(glob => `./${relative(userRoot, glob)}`)
const opts: GeneralImportGlobOptions = {
eager: true,
exhaustive: true,
base: '/',
...options,
}
return `import.meta.glob(${JSON.stringify(relativeGlobs)}, ${JSON.stringify(opts)})`
}
47 changes: 18 additions & 29 deletions packages/slidev/node/virtual/global-layers.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
import type { VirtualModuleTemplate } from './types'
import { existsSync } from 'node:fs'
import { join } from 'node:path'
import { toAtFS } from '../resolver'
import { makeAbsoluteImportGlob } from '../utils'

export const templateGlobalLayers: VirtualModuleTemplate = {
id: `/@slidev/global-layers`,
getContent({ roots }) {
const imports: string[] = []

let n = 0
function getComponent(names: string[]) {
const components = roots
.flatMap(root => names.map(name => join(root, name)))
.filter(i => existsSync(i))

imports.push(components.map((path, i) => `import __n${n}_${i} from '${toAtFS(path)}'`).join('\n'))
const render = components.map((_, i) => `h(__n${n}_${i})`).join(',')

n++

return `{ render: () => [${render}] }`
getContent({ userRoot, roots }) {
function* getComponent(name: string, names: string[]) {
yield `const ${name}Components = [\n`
for (const root of roots) {
const globs = names.map(name => join(root, `${name}.{ts,js,vue}`))
yield ' Object.values('
yield makeAbsoluteImportGlob(userRoot, globs, { import: 'default' })
yield ')[0],\n'
}
yield `].filter(Boolean)\n`
yield `export const ${name} = { render: () => ${name}Components.map(comp => h(comp)) }\n\n`
}

const globalTop = getComponent(['global.vue', 'global-top.vue', 'GlobalTop.vue'])
const globalBottom = getComponent(['global-bottom.vue', 'GlobalBottom.vue'])
const slideTop = getComponent(['slide-top.vue', 'SlideTop.vue'])
const slideBottom = getComponent(['slide-bottom.vue', 'SlideBottom.vue'])

return [
imports.join('\n'),
`import { h } from 'vue'`,
`export const GlobalTop = ${globalTop}`,
`export const GlobalBottom = ${globalBottom}`,
`export const SlideTop = ${slideTop}`,
`export const SlideBottom = ${slideBottom}`,
].join('\n')
`import { h } from 'vue'\n\n`,
...getComponent('GlobalTop', ['global', 'global-top', 'GlobalTop']),
...getComponent('GlobalBottom', ['global-bottom', 'GlobalBottom']),
...getComponent('SlideTop', ['slide-top', 'SlideTop']),
...getComponent('SlideBottom', ['slide-bottom', 'SlideBottom']),
].join('')
},
}
19 changes: 7 additions & 12 deletions packages/slidev/node/virtual/setups.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import type { VirtualModuleTemplate } from './types'
import { resolveSourceFiles, toAtFS } from '../resolver'
import { join } from 'node:path'
import { makeAbsoluteImportGlob } from '../utils'

function createSetupTemplate(name: string): VirtualModuleTemplate {
return {
id: `/@slidev/setups/${name}`,
getContent({ roots }) {
const setups = resolveSourceFiles(roots, `setup/${name}`)

const imports: string[] = []

setups.forEach((path, idx) => {
imports.push(`import __n${idx} from '${toAtFS(path)}'`)
getContent({ userRoot, roots }) {
const globs = roots.map((root) => {
const glob = join(root, `setup/${name}.{ts,js,mts,mjs}`)
return `Object.values(${makeAbsoluteImportGlob(userRoot, [glob], { import: 'default' })})[0]`
})

imports.push(`export default [${setups.map((_, idx) => `__n${idx}`).join(',')}]`)

return imports.join('\n')
return `export default [${globs.join(', ')}].filter(Boolean)`
},
}
}
Expand Down
24 changes: 7 additions & 17 deletions packages/slidev/node/virtual/styles.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { VirtualModuleTemplate } from './types'
import { existsSync } from 'node:fs'

import { join } from 'node:path'
import { resolveImportUrl, toAtFS } from '../resolver'
import { makeAbsoluteImportGlob } from '../utils'

export const templateStyle: VirtualModuleTemplate = {
id: '/@slidev/styles',
async getContent({ data, clientRoot, roots }) {
async getContent({ data, clientRoot, userRoot, roots }) {
function resolveUrlOfClient(name: string) {
return toAtFS(join(clientRoot, name))
}
Expand All @@ -20,20 +19,11 @@ export const templateStyle: VirtualModuleTemplate = {
]

for (const root of roots) {
const styles = [
join(root, 'styles', 'index.ts'),
join(root, 'styles', 'index.js'),
join(root, 'styles', 'index.css'),
join(root, 'styles.css'),
join(root, 'style.css'),
]

for (const style of styles) {
if (existsSync(style)) {
imports.push(`import "${toAtFS(style)}"`)
continue
}
}
imports.push(makeAbsoluteImportGlob(userRoot, [
join(root, 'styles/index.{ts,js,css}'),
join(root, 'styles.{ts,js,css}'),
join(root, 'style.{ts,js,css}'),
]))
}

if (data.features.katex)
Expand Down
5 changes: 3 additions & 2 deletions packages/slidev/node/vite/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ResolvedSlidevOptions, SlidevPluginOptions, SlidevServerOptions } from '@slidev/types'
import type { PluginOption } from 'vite'
import setupVitePlugins from '../setups/vite-plugins'
import { createVueCompilerFlagsPlugin } from './compilerFlagsVue'
import { createComponentsPlugin } from './components'
import { createContextInjectionPlugin } from './contextInjection'
Expand All @@ -17,7 +18,6 @@ import { createRemoteAssetsPlugin } from './remoteAssets'
import { createServerRefPlugin } from './serverRef'
import { createStaticCopyPlugin } from './staticCopy'
import { createUnocssPlugin } from './unocss'
import { createUserVitePlugins } from './userPlugins'
import { createVuePlugin } from './vue'

export function ViteSlidevPlugin(
Expand All @@ -43,7 +43,8 @@ export function ViteSlidevPlugin(
createUnocssPlugin(options, pluginOptions),
createStaticCopyPlugin(options, pluginOptions),
createInspectPlugin(options, pluginOptions),
createUserVitePlugins(options),
createPatchMonacoSourceMapPlugin(),

setupVitePlugins(options),
])
}
17 changes: 0 additions & 17 deletions packages/slidev/node/vite/userPlugins.ts

This file was deleted.

Loading
Loading