UNPKG

3.54 kBPlain TextView Raw
1import fs from 'fs-extra'
2import path from 'path'
3import mime from 'mime-types'
4
5import * as logger from './log'
6import { timer } from './timer'
7import { getRouteParams } from './getRouteParams'
8import { normalizeResponse } from './normalizeResponse'
9import { builtStaticFiles } from './builtStaticFiles'
10import { removeBuiltStaticFile } from './removeBuiltStaticFile'
11import { createLiveReloadScript } from './liveReloadScript'
12import { Env } from './constants'
13import { Presta } from './types'
14
15export function pathnameToFile(pathname: string, ext = 'html') {
16 return !!path.extname(pathname)
17 ? pathname // if path has extension, use it
18 : ext === 'html'
19 ? `${pathname}/index.html` // if HTML is inferred, create index
20 : `${pathname}.${ext}` // anything but HTML will need an extension, otherwise browsers will render as text
21}
22
23export function renderStaticEntries(entries: string[], config: Presta): Promise<{ allGeneratedFiles: string[] }> {
24 return new Promise(async (y, n) => {
25 logger.debug({
26 label: 'debug',
27 message: `rendering ${JSON.stringify(entries)}`,
28 })
29
30 const allGeneratedFiles: string[] = []
31 const devClient = createLiveReloadScript({ port: config.port })
32
33 for (const entry of entries) {
34 const location = entry.replace(config.cwd, '')
35
36 try {
37 delete require.cache[entry]
38
39 const file = require(entry)
40 const paths = await file.getStaticPaths()
41
42 const prevFiles = (builtStaticFiles[entry] = builtStaticFiles[entry] || [])
43 const nextFiles: string[] = []
44
45 if (!paths || !paths.length) {
46 logger.warn({
47 label: 'paths',
48 message: `${location} - no paths to render`,
49 })
50
51 prevFiles.forEach((file) => removeBuiltStaticFile(file, config))
52
53 continue
54 }
55
56 for (const url of paths) {
57 const time = timer()
58 const event = {
59 path: url,
60 routeParameters: file.route ? getRouteParams(url, file.route) : {},
61 }
62
63 const response = normalizeResponse(await file.handler(event, {}))
64 const type = response.headers ? response.headers['Content-Type'] : ''
65 const ext = type ? mime.extension(type as string) || 'html' : 'html'
66 const filename = pathnameToFile(url, ext)
67 const html = response.body + (config.env === Env.PRODUCTION ? '' : devClient)
68
69 fs.outputFileSync(path.join(config.staticOutputDir, filename), html, 'utf-8')
70
71 allGeneratedFiles.push(filename)
72 nextFiles.push(filename)
73
74 logger.info({
75 label: 'built',
76 message: url,
77 duration: time(),
78 })
79 }
80
81 // diff and remove files
82 for (const file of prevFiles) {
83 if (!nextFiles.includes(file)) {
84 removeBuiltStaticFile(file, config)
85 }
86 }
87
88 builtStaticFiles[entry] = nextFiles
89 } catch (e) {
90 if (config.env === 'development') {
91 logger.error({
92 label: 'error',
93 message: 'errors detected, pausing...',
94 error: e as Error,
95 })
96
97 y({ allGeneratedFiles })
98 } else {
99 logger.error({
100 label: 'error',
101 error: e as Error,
102 })
103
104 n(e)
105 }
106
107 // exit loop on any error
108 break
109 }
110 }
111
112 // clear to prevent memory leak
113 // loadCache.clearAllMemory() // TODO probs can't — emit?
114
115 y({ allGeneratedFiles })
116 })
117}