返回 slidev
options.ts
根目录 / packages / slidev / node / options.ts
1 import type { ResolvedSlidevOptions, ResolvedSlidevUtils, SlidevData, SlidevEntryOptions } from '@slidev/types'
2 import { objectMap, uniq } from '@antfu/utils'
3 import fg from 'fast-glob'
4 import { createDebug } from 'obug'
5 import path from 'pathe'
6 import pm from 'picomatch'
7 import { resolveAddons } from './integrations/addons'
8 import { getThemeMeta, resolveTheme } from './integrations/themes'
9 import { parser } from './parser'
10 import { getRoots, resolveEntry, toAtFS } from './resolver'
11 import setupIndexHtml from './setups/indexHtml'
12 import setupKatex from './setups/katex'
13 import setupShiki from './setups/shiki'
14
15 const RE_FILE_EXTENSION = /\.\w+$/
16
17 const debug = createDebug('slidev:options')
18
19 export async function resolveOptions(
20 entryOptions: SlidevEntryOptions,
21 mode: ResolvedSlidevOptions['mode'],
22 ): Promise<ResolvedSlidevOptions> {
23 const entry = await resolveEntry(entryOptions.entry)
24 const rootsInfo = await getRoots(entry)
25 const initialLoaded = await parser.load(
26 {
27 userRoot: rootsInfo.userRoot,
28 roots: [rootsInfo.userRoot],
29 allowedRoots: uniq([rootsInfo.userWorkspaceRoot, rootsInfo.userRoot]),
30 },
31 entry,
32 undefined,
33 mode,
34 )
35
36 // Load theme data first, because it may affect the config
37 let themeRaw = entryOptions.theme || initialLoaded.headmatter.theme as string | null | undefined
38 themeRaw = themeRaw === null ? 'none' : (themeRaw || 'default')
39 const [theme, themeRoot] = await resolveTheme(themeRaw, entry)
40 const themeRoots = themeRoot ? [themeRoot] : []
41 const themeMeta = themeRoot ? await getThemeMeta(theme, themeRoot) : undefined
42
43 const initialConfig = parser.resolveConfig(initialLoaded.headmatter, themeMeta, entryOptions.entry)
44 const addonRoots = await resolveAddons(initialConfig.addons)
45 const roots = uniq([...themeRoots, ...addonRoots, rootsInfo.userRoot])
46 const loaded = await parser.load(
47 {
48 userRoot: rootsInfo.userRoot,
49 roots,
50 allowedRoots: uniq([rootsInfo.userWorkspaceRoot, ...roots]),
51 },
52 entry,
53 undefined,
54 mode,
55 )
56 const config = parser.resolveConfig(loaded.headmatter, themeMeta, entryOptions.entry)
57
58 // These fields selected the roots used for the final load. Keep the
59 // returned config aligned with the graph that was actually loaded if a
60 // preparser changes them.
61 config.theme = themeRaw
62 config.addons = initialConfig.addons
63
64 if (entryOptions.download)
65 config.download ||= entryOptions.download
66
67 if (entryOptions.routerMode)
68 config.routerMode = entryOptions.routerMode
69
70 debug({
71 ...rootsInfo,
72 ...entryOptions,
73 config,
74 mode,
75 entry,
76 themeRaw,
77 theme,
78 themeRoots,
79 addonRoots,
80 roots,
81 })
82
83 const data: SlidevData = {
84 ...loaded,
85 config,
86 themeMeta,
87 }
88
89 const resolved: Omit<ResolvedSlidevOptions, 'utils'> = {
90 ...rootsInfo,
91 ...entryOptions,
92 data,
93 mode,
94 entry,
95 themeRaw,
96 theme,
97 themeRoots,
98 addonRoots,
99 roots,
100 }
101
102 return {
103 ...resolved,
104 utils: await createDataUtils(resolved),
105 }
106 }
107
108 export async function createDataUtils(resolved: Omit<ResolvedSlidevOptions, 'utils'>): Promise<ResolvedSlidevUtils> {
109 const monacoTypesIgnorePackagesMatches = (resolved.data.config.monacoTypesIgnorePackages || [])
110 .map(i => pm.makeRe(i))
111
112 let _layouts_cache_time = 0
113 let _layouts_cache: Promise<Record<string, string>> | null = null
114
115 return {
116 ...await setupShiki(resolved.roots),
117 katexOptions: await setupKatex(resolved.roots),
118 indexHtml: await setupIndexHtml(resolved),
119 define: getDefine(resolved),
120 iconsResolvePath: [resolved.clientRoot, ...resolved.roots].reverse(),
121 isMonacoTypesIgnored: pkg => monacoTypesIgnorePackagesMatches.some(i => i.test(pkg)),
122 getLayouts: () => {
123 const now = Date.now()
124 if (_layouts_cache && now - _layouts_cache_time < 2000)
125 return _layouts_cache
126 _layouts_cache_time = now
127 return _layouts_cache = worker()
128
129 async function worker() {
130 const layouts: Record<string, string> = {}
131 const layoutPaths = await Promise.all(
132 [resolved.clientRoot, ...resolved.roots]
133 .map(root => fg('layouts/**/*.{vue,js,mjs,ts,mts}', {
134 cwd: root,
135 absolute: true,
136 suppressErrors: true,
137 })),
138 )
139 for (const layoutPath of layoutPaths.flat(1)) {
140 const layoutName = path.basename(layoutPath).replace(RE_FILE_EXTENSION, '')
141 layouts[layoutName] = layoutPath
142 }
143 return layouts
144 }
145 },
146 }
147 }
148
149 function getDefine(options: Omit<ResolvedSlidevOptions, 'utils'>): Record<string, string> {
150 const matchMode = (mode: string | boolean) => mode === true || mode === options.mode
151 return objectMap(
152 {
153 __DEV__: options.mode === 'dev',
154 __SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
155 __SLIDEV_HASH_ROUTE__: options.data.config.routerMode === 'hash',
156 // Export navigates by URL, so memory routing (which ignores the URL) is
157 // disabled during export and falls back to history.
158 __SLIDEV_MEMORY_ROUTE__: options.data.config.routerMode === 'memory' && options.mode !== 'export',
159 __SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
160 __SLIDEV_FEATURE_EDITOR__: options.mode === 'dev' && options.data.config.editor !== false,
161 __SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
162 __SLIDEV_FEATURE_RECORD__: matchMode(options.data.config.record),
163 __SLIDEV_FEATURE_PRESENTER__: matchMode(options.data.config.presenter),
164 __SLIDEV_FEATURE_PRINT__: options.mode === 'export' || (options.mode === 'build' && [true, 'true', 'auto'].includes(options.data.config.download)),
165 __SLIDEV_FEATURE_BROWSER_EXPORTER__: matchMode(options.data.config.browserExporter),
166 __SLIDEV_FEATURE_WAKE_LOCK__: matchMode(options.data.config.wakeLock),
167 __SLIDEV_FEATURE_PWA__: matchMode(options.data.config.pwa),
168 __SLIDEV_HAS_SERVER__: options.mode !== 'build',
169 },
170 (v, k) => [v, JSON.stringify(k)],
171 )
172 }
173
173 lines TYPESCRIPT