-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathcli.js
161 lines (140 loc) · 5.22 KB
/
cli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env node
const { JSDOM } = require('jsdom')
const fs = require('fs-extra')
const marked = require('marked')
const http = require('http')
const chokidar = require('chokidar')
const fm = require('front-matter')
// attributes: { template: "custom.html" }
// body: "# My normal markdown ..."
const scriptArgs = process.argv.slice(2)
const command = scriptArgs[0]
switch (command) {
case 'build':
build()
break
case 'develop':
develop(scriptArgs[1] ? Number(scriptArgs[1]) : 8000)
break
case 'init':
init()
break
default:
console.log(`Command 'teeny ${command}' does not exist.`)
process.exit(1)
}
async function build() {
await fs.emptyDir('public/')
await safeExecute(
async () =>
await fs.copy('templates/', 'public/', { filter: (f) => !f.startsWith('.') && !f.endsWith('.html') })
)
await safeExecute(
async () => await fs.copy('pages/', 'public/', { filter: (f) => !f.startsWith('.') && !f.endsWith('.md') })
)
await safeExecute(async () => await fs.copy('static/', 'public/'), { filter: (f) => !f.startsWith('.') })
await processDirectory('pages')
}
async function processDirectory(directoryPath) {
let contents = await fs.readdir(`${directoryPath}/`)
const processPagePromises = []
for (const element of contents) {
const isDirectory = (await fs.lstat(`${directoryPath}/${element}`)).isDirectory()
if (isDirectory) {
await processDirectory(`${directoryPath}/${element}`, processPagePromises)
continue
}
processPagePromises.push(processPage(`${directoryPath}/${element}`))
}
await Promise.all(processPagePromises)
}
async function develop(port) {
await build()
const server = startServer(port)
const watcher = chokidar.watch(['pages/', 'static/', 'templates/']).on('change', async (path, _) => {
console.log(`Detected change in file ${path}. Restarting development server.`)
server.close()
await watcher.close()
await develop(port)
})
}
async function init() {
await safeExecute(async () => await fs.mkdir('pages/'))
await safeExecute(async () => await fs.mkdir('static/'))
await safeExecute(async () => await fs.mkdir('templates/'))
const examplePage = `---\ntemplate: homepage\n---\n# Hello World`
const exampleTemplate = `<html><body><p>My first Teeny page</p><div id='page-content'></div><script type="text/javascript" src='main.js'></body></html>`
const defaultTemplate = `<html><body><div id='page-content'></div></body></html>`
const exampleStaticAssetJs = `console.log('hello world')`
await fs.writeFile('pages/index.md', examplePage)
await fs.writeFile('templates/homepage.html', exampleTemplate)
await fs.writeFile('templates/default.html', defaultTemplate)
await fs.writeFile('static/main.js', exampleStaticAssetJs)
}
async function processPage(pagePath) {
let templatePath = 'templates/default.html'
const fileData = await fs.readFile(pagePath, 'utf-8')
const { attributes: frontmatter, body: markdown } = await fm(fileData)
if (frontmatter.template) {
templatePath = `templates/${frontmatter.template}.html`
}
const dom = await JSDOM.fromFile(templatePath)
const parsedHtml = marked(markdown)
const document = dom.window.document
const pageContentElement = document.getElementById('page-content')
if (pageContentElement) {
pageContentElement.innerHTML = parsedHtml
} else {
console.log(
`Could not find element with id 'page-content' in template ${templatePath}. Generating page without markdown content.`
)
}
const wrapperHtmlElement = document.getElementsByTagName('html')
if (!wrapperHtmlElement.length) {
console.log(`Templates should contain the 'html' tag.`)
process.exit(1)
}
let title = frontmatter.title
if (!title) {
const h1s = document.getElementsByTagName('h1')
if (h1s.length) {
title = h1s[0].innerHTML
}
}
if (title) {
document.title = title
}
const finalHtml = document.getElementsByTagName('html')[0].outerHTML
const pagePathParts = pagePath.replace('pages/', '').split('/')
const pageName = pagePathParts.pop().split('.md')[0]
const targetPath = pagePathParts.join('/')
await fs.writeFile(`public/${targetPath}/${pageName}.html`, finalHtml)
}
function startServer(port) {
console.log(`Development server starting on http://localhost:${port}`)
return http
.createServer(function (req, res) {
const url = req.url
let filePath = url
if (url === '/') {
filePath = '/index.html'
} else if (!url.includes('.')) {
filePath += '.html'
}
fs.readFile('public' + filePath, function (err, data) {
if (err) {
res.writeHead(404)
res.end('<h1>404: Page not found</h1>')
return
}
res.writeHead(200)
res.end(data)
})
})
.listen(port)
}
async function safeExecute(func) {
try {
await func()
} catch {}
}