@lincyaw/web 1.0.0-rc.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -4
- package/package.json +20 -19
- package/src/app/base.css +117 -0
- package/src/app/block-playback.ts +81 -0
- package/src/app/block-report.ts +104 -0
- package/src/app/document-modules.ts +30 -0
- package/src/app/env.d.ts +12 -0
- package/src/app/main.ts +198 -0
- package/src/app/page-app.ts +166 -0
- package/src/app/router.ts +109 -0
- package/src/app/server.ts +32 -0
- package/src/app/site/home.tsx +117 -0
- package/src/app/site/layout.tsx +92 -0
- package/src/app/site/search.tsx +22 -0
- package/src/app/site.css +1450 -0
- package/src/client/document.css +172 -0
- package/src/client/host.ts +22 -94
- package/src/client/preview-frame.ts +152 -0
- package/src/client/runtime.ts +159 -0
- package/src/domain/app-config.ts +91 -0
- package/src/domain/config.ts +17 -9
- package/src/domain/dev-updates.ts +12 -0
- package/src/domain/index.ts +17 -7
- package/src/domain/landing.ts +83 -0
- package/src/domain/links.ts +23 -18
- package/src/domain/messages.ts +74 -63
- package/src/domain/pages.ts +23 -41
- package/src/domain/themes.ts +59 -0
- package/src/domain/workspace-home.ts +12 -0
- package/src/node/client-modules.ts +92 -0
- package/src/node/document-bundle.ts +227 -0
- package/src/node/landing-template.ts +34 -0
- package/src/node/landing.ts +82 -0
- package/src/node/offline.ts +34 -11
- package/src/node/publish.ts +83 -15
- package/src/node/renderer.ts +78 -117
- package/src/node/site-assets.ts +17 -0
- package/src/node/site-build.ts +304 -0
- package/src/node/site-files.ts +87 -0
- package/src/node/site-model.ts +337 -0
- package/src/node/site-page.ts +82 -0
- package/src/node/site-server.ts +822 -0
- package/src/node/slides.ts +135 -0
- package/src/node/viewer-app.ts +504 -0
- package/src/node/viewer-build.ts +38 -0
- package/custom-nav-controls.vue +0 -17
- package/src/client/env.d.ts +0 -1
- package/src/client/home.ts +0 -182
- package/src/client/index.ts +0 -4
- package/src/client/page.ts +0 -38
- package/src/client/preview.ts +0 -71
- package/src/client/style.css +0 -427
- package/src/client/theme.ts +0 -322
- package/src/node/addons.ts +0 -76
- package/src/node/site.ts +0 -349
- package/src/node/sites.ts +0 -531
- package/src/node/vite.ts +0 -166
- package/src/node/vitepress.ts +0 -72
package/src/node/site.ts
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, utimesSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import { createRequire } from 'node:module'
|
|
3
|
-
import { dirname, join, sep } from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
import { deckLocale, previewFrameAncestors } from '@lincyaw/kernel'
|
|
6
|
-
import type { IRDocument } from '@lincyaw/kernel'
|
|
7
|
-
import { FONT_LICENSE, fontLicense, optimizeDeps, packageOf } from '@lincyaw/kernel/node'
|
|
8
|
-
import { formOptions, neighbour, pageMarkdown, searchDocuments, SERVER_PROXY, sidebar, SITE_LABELS, toPages, translationPages } from '../domain/index.ts'
|
|
9
|
-
import type { FormOptions, NavItem, Neighbour, SearchDocument, SiteShell, WebPage, WebRendererConfig } from '../domain/index.ts'
|
|
10
|
-
import type { ClientModules } from './addons.ts'
|
|
11
|
-
|
|
12
|
-
const CLIENT_ENTRY = fileURLToPath(new URL('../client/index.ts', import.meta.url))
|
|
13
|
-
const PREVIEW_AGENT = fileURLToPath(new URL('../client/preview.ts', import.meta.url))
|
|
14
|
-
const SEARCH = createRequire(import.meta.url).resolve('@lincyaw/kernel/reading')
|
|
15
|
-
const VITE_PLUGIN = fileURLToPath(new URL('./vite.ts', import.meta.url))
|
|
16
|
-
const GENERATED = '// Generated by @lincyaw/web from the IR; do not edit.'
|
|
17
|
-
const WEB_DIR = fileURLToPath(new URL('../..', import.meta.url))
|
|
18
|
-
|
|
19
|
-
export function siteFiles(workDir: string) {
|
|
20
|
-
return {
|
|
21
|
-
root: join(workDir, 'site'),
|
|
22
|
-
ir: join(workDir, 'ir.json'),
|
|
23
|
-
searchDocuments: join(workDir, 'search-documents.json'),
|
|
24
|
-
site: join(workDir, 'site.json'),
|
|
25
|
-
publicDir: join(workDir, 'site', 'public'),
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const SRC_EXCLUDE = ['public/**']
|
|
30
|
-
|
|
31
|
-
export function presmiumRoot(): string {
|
|
32
|
-
const own = realpathSync(WEB_DIR)
|
|
33
|
-
let outermost: string | undefined
|
|
34
|
-
for (let dir = own; dirname(dir) !== dir; dir = dirname(dir)) {
|
|
35
|
-
if (existsSync(join(dir, 'pnpm-workspace.yaml'))) return dir
|
|
36
|
-
if (dir.split(sep).at(-1) === 'node_modules') outermost = dirname(dir)
|
|
37
|
-
}
|
|
38
|
-
return outermost ?? own
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function allowed(...dirs: (string | undefined)[]): string[] {
|
|
42
|
-
return [...new Set(dirs.filter((dir): dir is string => Boolean(dir)).map(dir => (existsSync(dir) ? realpathSync(dir) : dir)))]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function write(file: string, content: string | Buffer): void {
|
|
46
|
-
if (existsSync(file) && readFileSync(file).equals(Buffer.from(content))) return
|
|
47
|
-
mkdirSync(dirname(file), { recursive: true })
|
|
48
|
-
writeFileSync(file, content)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function copyDir(from: string, to: string): void {
|
|
52
|
-
for (const file of readdirSync(from, { recursive: true, encoding: 'utf8' })) {
|
|
53
|
-
const source = join(from, file)
|
|
54
|
-
const stat = statSync(source)
|
|
55
|
-
if (!stat.isFile()) continue
|
|
56
|
-
const target = join(to, file)
|
|
57
|
-
const copied = existsSync(target) ? statSync(target) : undefined
|
|
58
|
-
if (copied && copied.size === stat.size && Math.abs(copied.mtimeMs - stat.mtimeMs) < 1) continue
|
|
59
|
-
mkdirSync(dirname(target), { recursive: true })
|
|
60
|
-
writeFileSync(target, readFileSync(source))
|
|
61
|
-
utimesSync(target, stat.atime, stat.mtime)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function linkDependencies(root: string): void {
|
|
66
|
-
const require = createRequire(import.meta.url)
|
|
67
|
-
for (const name of ['vitepress', 'vue']) {
|
|
68
|
-
const target = dirname(require.resolve(`${name}/package.json`))
|
|
69
|
-
const link = join(root, 'node_modules', name)
|
|
70
|
-
if (existsSync(link) && readlinkSync(link) === target) continue
|
|
71
|
-
rmSync(link, { force: true })
|
|
72
|
-
mkdirSync(dirname(link), { recursive: true })
|
|
73
|
-
symlinkSync(target, link, 'junction')
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function browserPackages(modules: ClientModules): string[] {
|
|
78
|
-
const files = [...modules.plugins, ...modules.viewers.map(({ file }) => file), ...modules.components.values()]
|
|
79
|
-
return [...new Set([WEB_DIR, ...files.flatMap(file => packageOf(file) ?? [])])]
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function languageLabel(lang: string): string {
|
|
83
|
-
const primary = lang.split('-')[0]!
|
|
84
|
-
try {
|
|
85
|
-
return new Intl.DisplayNames([lang], { type: 'language' }).of(primary) ?? lang
|
|
86
|
-
} catch {
|
|
87
|
-
return lang
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface SiteLocale {
|
|
92
|
-
lang: string
|
|
93
|
-
label: string
|
|
94
|
-
title: string
|
|
95
|
-
sidebar: ReturnType<typeof sidebar>
|
|
96
|
-
labels: (typeof SITE_LABELS)[keyof typeof SITE_LABELS]
|
|
97
|
-
nav?: NavItem[]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface SiteData {
|
|
101
|
-
title: string
|
|
102
|
-
sidebar: ReturnType<typeof sidebar>
|
|
103
|
-
form: FormOptions
|
|
104
|
-
label: string
|
|
105
|
-
locales: SiteLocale[]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
interface Translation {
|
|
109
|
-
lang: string
|
|
110
|
-
doc: IRDocument
|
|
111
|
-
pages: WebPage[]
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function siteData(doc: IRDocument, options: WebRendererConfig, translations: Translation[], workspace?: WorkspaceOptions): SiteData {
|
|
115
|
-
const form = formOptions(doc.config.presmium, doc.config.renderers)
|
|
116
|
-
return {
|
|
117
|
-
title: doc.meta.title,
|
|
118
|
-
sidebar: sidebar(doc, toPages(doc)),
|
|
119
|
-
form: workspace ? { ...form, slides: Boolean(form.slidesUrl) } : form,
|
|
120
|
-
label: languageLabel(options.lang),
|
|
121
|
-
locales: translations.map(({ lang, doc: translated, pages }) => ({
|
|
122
|
-
lang,
|
|
123
|
-
label: languageLabel(lang),
|
|
124
|
-
title: translated.meta.title,
|
|
125
|
-
sidebar: sidebar(translated, pages, `/${lang}`),
|
|
126
|
-
labels: SITE_LABELS[deckLocale(lang)],
|
|
127
|
-
...options.shell.nav && workspace ? { nav: relativeNav(options.shell.nav, depthOf(workspace.document) + 1) } : {},
|
|
128
|
-
})),
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface WorkspaceOptions {
|
|
133
|
-
document: string
|
|
134
|
-
home: string
|
|
135
|
-
endpoints: { ir: string, source: string }
|
|
136
|
-
served?: boolean
|
|
137
|
-
allow?: string[]
|
|
138
|
-
neighbours?: { prev?: Neighbour, next?: Neighbour }
|
|
139
|
-
search?: SearchDocument[]
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface DeckPaths {
|
|
143
|
-
deckDir: string
|
|
144
|
-
workspaceRoot: string
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function depthOf(dir: string): number {
|
|
148
|
-
return dir ? dir.split('/').length : 0
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function fromPage(link: string, depth: number): string {
|
|
152
|
-
if (!link.startsWith('/') || link.startsWith('//')) return link
|
|
153
|
-
return `${'../'.repeat(depth)}${link.slice(1)}` || './'
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function relativeNav(items: NavItem[], depth: number): NavItem[] {
|
|
157
|
-
return items.map(item => ({ ...item, ...item.link ? { link: fromPage(item.link, depth) } : {}, ...item.items ? { items: relativeNav(item.items, depth) } : {} }))
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function neighbourFrom(other: Neighbour | undefined, depth: number): Neighbour | undefined {
|
|
161
|
-
return other && { text: other.text, link: fromPage(`/${other.link}`, depth) }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function shellConfig(shell: SiteShell, base: string, image: string | undefined, workspace?: WorkspaceOptions): { site: Record<string, unknown>, ogTitle: boolean } {
|
|
165
|
-
const site: Record<string, unknown> = {}
|
|
166
|
-
const themeConfig: Record<string, unknown> = {}
|
|
167
|
-
if (shell.nav) themeConfig.nav = workspace ? relativeNav(shell.nav, depthOf(workspace.document)) : shell.nav
|
|
168
|
-
if (shell.footer) themeConfig.footer = shell.footer
|
|
169
|
-
const head: unknown[] = []
|
|
170
|
-
if (shell.head) {
|
|
171
|
-
head.push(['meta', { property: 'og:type', content: 'website' }])
|
|
172
|
-
if (shell.head.description) {
|
|
173
|
-
site.description = shell.head.description
|
|
174
|
-
head.push(['meta', { property: 'og:description', content: shell.head.description }])
|
|
175
|
-
}
|
|
176
|
-
if (image) head.push(['meta', { property: 'og:image', content: image }])
|
|
177
|
-
}
|
|
178
|
-
if (shell.sitemap && !workspace) site.sitemap = { hostname: `${shell.sitemap.hostname}${base}` }
|
|
179
|
-
return { site: { ...site, head, themeConfig }, ogTitle: Boolean(shell.head) }
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export const OG_TITLE = `site.transformHead = ({ title }) => [['meta', { property: 'og:title', content: title }]]\n`
|
|
183
|
-
|
|
184
|
-
function config(options: WebRendererConfig, files: ReturnType<typeof siteFiles>, modules: ClientModules, deck: DeckPaths, workspace: WorkspaceOptions | undefined, image: string | undefined, optimize: { include: string[], exclude: string[] }, preview: SiteInput['preview']): string {
|
|
185
|
-
const labels = SITE_LABELS[deckLocale(options.lang)]
|
|
186
|
-
const shell = shellConfig(options.shell, options.base, image, workspace)
|
|
187
|
-
const site = {
|
|
188
|
-
lang: options.lang,
|
|
189
|
-
base: options.base,
|
|
190
|
-
cleanUrls: false,
|
|
191
|
-
ignoreDeadLinks: true,
|
|
192
|
-
srcExclude: SRC_EXCLUDE,
|
|
193
|
-
...shell.site,
|
|
194
|
-
themeConfig: { ...labels.themeConfig, ...shell.site.themeConfig as object },
|
|
195
|
-
}
|
|
196
|
-
const imports = [
|
|
197
|
-
...modules.vite.map((file, i) => `import vite${i} from ${JSON.stringify(file)}\n`),
|
|
198
|
-
...modules.nodeVite.map(({ file }, i) => `import node${i} from ${JSON.stringify(file)}\n`),
|
|
199
|
-
].join('')
|
|
200
|
-
const nodePlugins = modules.nodeVite.map(({ config: options }, i) =>
|
|
201
|
-
`...(node${i}.vitePlugins?.({ ...deck, config: ${JSON.stringify(options)} }) ?? [])`)
|
|
202
|
-
const server = options.server ? `
|
|
203
|
-
site.themeConfig.presmium.server = true
|
|
204
|
-
site.vite.server = { proxy: { ${JSON.stringify(SERVER_PROXY)}: { target: ${JSON.stringify(options.server)}, ws: true } }, fs: { allow: ${JSON.stringify(allowed(files.root, deck.deckDir, deck.workspaceRoot, presmiumRoot()))} } }
|
|
205
|
-
` : ''
|
|
206
|
-
const inWorkspace = workspace ? `
|
|
207
|
-
Object.assign(site.themeConfig.presmium, ${JSON.stringify({ document: workspace.document, home: workspace.home, ir: workspace.endpoints.ir, ...workspace.served ? { server: true } : {} })})
|
|
208
|
-
${workspace.served ? `site.vite.server = { fs: { allow: ${JSON.stringify(workspace.allow ?? [])} } }
|
|
209
|
-
` : ''}` : ''
|
|
210
|
-
const framed = preview ? `site.vite.plugins.push(presmiumFrame(${JSON.stringify(previewFrameAncestors(preview.origins))}))
|
|
211
|
-
` : ''
|
|
212
|
-
return `${GENERATED}
|
|
213
|
-
import { readFileSync } from 'node:fs'
|
|
214
|
-
import { tokenize } from ${JSON.stringify(SEARCH)}
|
|
215
|
-
import { presmiumFrame, presmiumIR } from ${JSON.stringify(VITE_PLUGIN)}
|
|
216
|
-
${imports}
|
|
217
|
-
const site = ${JSON.stringify(site, null, 2)}
|
|
218
|
-
|
|
219
|
-
// Read when the config loads, so this file stays the same while the IR changes.
|
|
220
|
-
const data = JSON.parse(readFileSync(${JSON.stringify(files.site)}, 'utf8'))
|
|
221
|
-
site.title = data.title
|
|
222
|
-
site.themeConfig.sidebar = data.sidebar
|
|
223
|
-
site.themeConfig.presmium = { form: data.form }
|
|
224
|
-
|
|
225
|
-
// The translations (P-123): a locale each, with its title, sidebar and labels; the language menu keeps the page.
|
|
226
|
-
const locales = data.locales.map(locale => locale.lang)
|
|
227
|
-
if (locales.length > 0) {
|
|
228
|
-
site.locales = { root: { label: data.label, lang: site.lang } }
|
|
229
|
-
for (const locale of data.locales) {
|
|
230
|
-
site.locales[locale.lang] = { label: locale.label, lang: locale.lang, title: locale.title, themeConfig: { ...locale.labels.themeConfig, sidebar: locale.sidebar, ...locale.nav ? { nav: locale.nav } : {} } }
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// The Vite plugins of the deck's plugin packages: their \`vite.config.ts\`, as in the slides form, and those
|
|
235
|
-
// their Node entry adds (\`vitePlugins\`), with the plugin's configuration.
|
|
236
|
-
const deck = ${JSON.stringify(deck)}
|
|
237
|
-
site.vite = {
|
|
238
|
-
plugins: [
|
|
239
|
-
presmiumIR(${JSON.stringify(files.ir)}, ${JSON.stringify(files.searchDocuments)}, ${workspace ? JSON.stringify(workspace.endpoints) : 'undefined'}, locales),
|
|
240
|
-
...[${modules.vite.map((_, i) => `vite${i}`).join(', ')}].flatMap(config => config?.plugins ?? []),${nodePlugins.map(plugin => `\n ${plugin},`).join('')}
|
|
241
|
-
],
|
|
242
|
-
resolve: { dedupe: ['vue'] },${optimize.exclude.length > 0 ? `
|
|
243
|
-
// The Presmium packages installed under node_modules, as from npm: their browser dependencies
|
|
244
|
-
// pre-bundled, themselves never (the pages load their source).
|
|
245
|
-
optimizeDeps: ${JSON.stringify(optimize)},` : ''}
|
|
246
|
-
}
|
|
247
|
-
${server}${inWorkspace}${framed}${shell.ogTitle ? OG_TITLE : ''}
|
|
248
|
-
// Local search over the IR text of each section: titles, narration, prose and code. The index is
|
|
249
|
-
// the renderer's own (see presmiumIR); the search box tokenizes queries as it does, Chinese into words.
|
|
250
|
-
site.themeConfig.search = {
|
|
251
|
-
provider: 'local',
|
|
252
|
-
options: {
|
|
253
|
-
miniSearch: { options: { tokenize } },${labels.search ? `
|
|
254
|
-
translations: ${JSON.stringify(labels.search)},` : ''}
|
|
255
|
-
locales: Object.fromEntries(data.locales.filter(locale => locale.labels.search).map(locale => [locale.lang, { translations: locale.labels.search }])),
|
|
256
|
-
},
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export default site
|
|
260
|
-
`
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function theme(preview?: { origins: string[], document: string }): string {
|
|
264
|
-
return `${GENERATED}
|
|
265
|
-
import doc, { revision, translations } from 'virtual:presmium/ir'
|
|
266
|
-
import { createPresmiumTheme } from ${JSON.stringify(CLIENT_ENTRY)}
|
|
267
|
-
${preview ? `import { startWebPreview } from ${JSON.stringify(PREVIEW_AGENT)}
|
|
268
|
-
` : ''}import { components, plugins, viewers } from './addons.ts'
|
|
269
|
-
|
|
270
|
-
export default createPresmiumTheme({ doc, revision, translations, components, plugins, viewers${preview ? `, preview: page => startWebPreview(page, ${JSON.stringify(preview)})` : ''} })
|
|
271
|
-
`
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function addons(modules: ClientModules): string {
|
|
275
|
-
const load = (file: string) => `() => import(${JSON.stringify(file)})`
|
|
276
|
-
const components = [...modules.components].map(([name, file]) => ` ${JSON.stringify(name)}: ssr ? never : ${load(file)},`)
|
|
277
|
-
return `${GENERATED}
|
|
278
|
-
const ssr = import.meta.env.SSR
|
|
279
|
-
const never = () => new Promise(() => {})
|
|
280
|
-
|
|
281
|
-
export const components = {
|
|
282
|
-
${components.join('\n')}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export const plugins = ssr ? [] : [
|
|
286
|
-
${modules.plugins.map(file => ` ${load(file)},`).join('\n')}
|
|
287
|
-
]
|
|
288
|
-
|
|
289
|
-
export const viewers = ssr ? [] : [
|
|
290
|
-
${modules.viewers.map(({ name, file }) => ` { name: ${JSON.stringify(name)}, load: ${load(file)} },`).join('\n')}
|
|
291
|
-
]
|
|
292
|
-
`
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export interface SiteInput {
|
|
296
|
-
doc: IRDocument
|
|
297
|
-
deck: DeckPaths
|
|
298
|
-
modules: ClientModules
|
|
299
|
-
options: WebRendererConfig
|
|
300
|
-
workspace?: WorkspaceOptions
|
|
301
|
-
translations?: { lang: string, doc: IRDocument }[]
|
|
302
|
-
image?: string
|
|
303
|
-
preview?: { origins: string[] }
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export function localeFile(file: string, lang: string): string {
|
|
307
|
-
return file.replace(/\.json$/, `.${lang}.json`)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function writePages(dir: string, pages: WebPage[], prefix: string, depth: number, workspace?: WorkspaceOptions): void {
|
|
311
|
-
const names = new Set(pages.map(page => `${page.path}.md`))
|
|
312
|
-
if (existsSync(dir)) for (const file of readdirSync(dir).filter(f => f.endsWith('.md') && !names.has(f))) rmSync(join(dir, file))
|
|
313
|
-
pages.forEach((page, i) => {
|
|
314
|
-
const prev = neighbour(pages[i - 1], prefix) ?? neighbourFrom(workspace?.neighbours?.prev, depth)
|
|
315
|
-
const next = neighbour(pages[i + 1], prefix) ?? neighbourFrom(workspace?.neighbours?.next, depth)
|
|
316
|
-
write(join(dir, `${page.path}.md`), pageMarkdown(page, prev, next))
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function writeSite(workDir: string, input: SiteInput): void {
|
|
321
|
-
const files = siteFiles(workDir)
|
|
322
|
-
const { doc, modules, options, workspace } = input
|
|
323
|
-
const pages = toPages(doc)
|
|
324
|
-
const translations: Translation[] = (input.translations ?? []).map(t => ({ ...t, pages: translationPages(t.doc, pages) }))
|
|
325
|
-
const others = workspace?.search ?? []
|
|
326
|
-
write(files.ir, JSON.stringify(doc))
|
|
327
|
-
write(files.searchDocuments, JSON.stringify([...searchDocuments(doc, options.base), ...others]))
|
|
328
|
-
for (const { lang, doc: translated, pages: own } of translations) {
|
|
329
|
-
write(localeFile(files.ir, lang), JSON.stringify(translated))
|
|
330
|
-
write(localeFile(files.searchDocuments, lang), JSON.stringify([...searchDocuments(translated, `${options.base}${lang}/`, own), ...others]))
|
|
331
|
-
}
|
|
332
|
-
write(files.site, JSON.stringify(siteData(doc, options, translations, workspace)))
|
|
333
|
-
write(join(workDir, '.gitignore'), '*\n')
|
|
334
|
-
linkDependencies(files.root)
|
|
335
|
-
const optimize = optimizeDeps(files.root, browserPackages(modules))
|
|
336
|
-
write(join(files.root, '.vitepress/config.mjs'), config(options, files, modules, input.deck, workspace, input.image, optimize, input.preview))
|
|
337
|
-
write(join(files.root, '.vitepress/theme/index.ts'), theme(input.preview && { origins: input.preview.origins, document: workspace?.document ?? '' }))
|
|
338
|
-
write(join(files.root, '.vitepress/theme/addons.ts'), addons(modules))
|
|
339
|
-
const depth = depthOf(workspace?.document ?? '')
|
|
340
|
-
writePages(files.root, pages, '', depth, workspace)
|
|
341
|
-
const langs = new Set(translations.map(t => t.lang))
|
|
342
|
-
for (const entry of readdirSync(files.root, { withFileTypes: true })) {
|
|
343
|
-
if (entry.isDirectory() && !entry.name.startsWith('.') && !['public', 'node_modules'].includes(entry.name) && !langs.has(entry.name)) rmSync(join(files.root, entry.name), { recursive: true, force: true })
|
|
344
|
-
}
|
|
345
|
-
for (const { lang, pages: own } of translations) writePages(join(files.root, lang), own, `/${lang}`, depth + 1, workspace)
|
|
346
|
-
const deckPublic = join(input.deck.deckDir, 'public')
|
|
347
|
-
if (existsSync(deckPublic)) copyDir(deckPublic, files.publicDir)
|
|
348
|
-
write(join(files.publicDir, FONT_LICENSE), fontLicense(import.meta.url))
|
|
349
|
-
}
|