vowel 0.2.1-beta.1 → 0.2.3
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/.github/ISSUE_TEMPLATE/bug_report.md +73 -0
- package/.github/ISSUE_TEMPLATE/config.yml +7 -0
- package/README.md +94 -0
- package/docs-source/.vercel/README.txt +11 -0
- package/docs-source/.vercel/project.json +1 -0
- package/getMetadata.js +41 -0
- package/index.js +140 -24
- package/package.json +7 -1
- package/regex.js +0 -8
- package/utils.js +6 -4
- package/books/.votive.db +0 -0
- package/books/A.W. Tozer/The Pursuit of God.md +0 -628
- package/books/Abbe/314/201 Pre/314/201vost/Manon Lescaut.md" +0 -1201
- package/books/Abbie Farwell Brown/The Christmas Angel.md +0 -861
- package/books/Abraham Merritt/The Moon Pool.md +0 -4958
- package/books/Adalbert Stifter/Rock Crystal.md +0 -534
- package/books/Adam Seaborn/Symzonia: A Voyage of Discovery.md +0 -848
- package/books/Adam Smith/The Wealth of Nations.md +0 -4419
- package/docs-source/.votive.db +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report a bug to help improve Vowel.
|
|
4
|
+
title: ""
|
|
5
|
+
labels: "bug"
|
|
6
|
+
assignees: "samlfair"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
Thanks for taking the time to help build Vowel.
|
|
11
|
+
A well-detailed bug report helps us to solve the problem quickly.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
### Versions
|
|
15
|
+
|
|
16
|
+
- vowel: <!-- eg: v0.1.32 -->
|
|
17
|
+
- node: <!-- eg: v14.15.0 -->
|
|
18
|
+
- os: <!-- eg: macOS 14.5 -->
|
|
19
|
+
- browser: <!-- eg: Chrome 129 -->
|
|
20
|
+
|
|
21
|
+
### Browser report
|
|
22
|
+
|
|
23
|
+
<!--
|
|
24
|
+
Generate a browser report at https://www.whatsmybrowser.org/
|
|
25
|
+
Copy-paste a link to the report
|
|
26
|
+
-->
|
|
27
|
+
|
|
28
|
+
### Link to Stackblitz
|
|
29
|
+
|
|
30
|
+
<!--
|
|
31
|
+
If possible, upload a reproduction to Stackblitz
|
|
32
|
+
and copy-paste the link here
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
### Steps to reproduce
|
|
36
|
+
|
|
37
|
+
<!-- eg: "Go to X and click on Y" -->
|
|
38
|
+
|
|
39
|
+
### Expected behavior
|
|
40
|
+
|
|
41
|
+
<!-- eg: "The image will appear below the headline" -->
|
|
42
|
+
|
|
43
|
+
### Actual behavior
|
|
44
|
+
|
|
45
|
+
<!-- eg: "The image appears above the headline" -->
|
|
46
|
+
|
|
47
|
+
### Additional technologies
|
|
48
|
+
|
|
49
|
+
<!-- List any other technologies you're using with Vowel -->
|
|
50
|
+
|
|
51
|
+
### Screenshot
|
|
52
|
+
|
|
53
|
+
<!-- If possible, include a screenshot of the issue -->
|
|
54
|
+
|
|
55
|
+
### Errors from terminal
|
|
56
|
+
|
|
57
|
+
<!-- If there are any error messages in the terminal, paste them here -->
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Errors from browser console
|
|
64
|
+
|
|
65
|
+
<!-- If there are any error message in the error console, paste them here -->
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Additional comments
|
|
72
|
+
|
|
73
|
+
<!-- Include any other relevent information -->
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Vowel
|
|
2
|
+
|
|
3
|
+
*Markdown websites in milliseconds*
|
|
4
|
+
|
|
5
|
+
- Bundled with [Votive](https://github.com/samlfair/votive)
|
|
6
|
+
- Served with [Voot](https://github.com/samlfair/voot)
|
|
7
|
+
|
|
8
|
+
## Roadmap
|
|
9
|
+
|
|
10
|
+
### High priority
|
|
11
|
+
|
|
12
|
+
- [ ] robots.txt
|
|
13
|
+
- [ ] sitemap.xml
|
|
14
|
+
- [ ] 404.html
|
|
15
|
+
- [ ] TUI
|
|
16
|
+
- [ ] Create settings.md
|
|
17
|
+
- [ ] Site title
|
|
18
|
+
- [ ] Domain
|
|
19
|
+
- [ ] Webmentions
|
|
20
|
+
- [ ] Logo
|
|
21
|
+
- [ ] Wordmark
|
|
22
|
+
- [ ] Identity (rel=me)
|
|
23
|
+
- [ ] Filename breadcrumbs
|
|
24
|
+
- [ ] RSS
|
|
25
|
+
- [ ] Sitemap
|
|
26
|
+
- [ ] Create home.md
|
|
27
|
+
- [ ] Create folder settings files
|
|
28
|
+
- [ ] Title
|
|
29
|
+
- [ ] Breadcrumb
|
|
30
|
+
- [ ] Custom CSS
|
|
31
|
+
- [ ] Tags
|
|
32
|
+
- [ ] Customize index fallback
|
|
33
|
+
- [ ] Date format settings
|
|
34
|
+
- [ ] ::mark::
|
|
35
|
+
- [ ] Infer images
|
|
36
|
+
- [ ] Favicon
|
|
37
|
+
- [ ] Webmentions
|
|
38
|
+
- [ ] HTML boilerplate
|
|
39
|
+
- [ ] Page lists
|
|
40
|
+
- [ ] View transitions
|
|
41
|
+
- [ ] Logo
|
|
42
|
+
- [ ] Wordmark
|
|
43
|
+
- [ ] Sort nav items
|
|
44
|
+
- [ ] Canonical URL
|
|
45
|
+
- [ ] Handle external links
|
|
46
|
+
- [ ] Admonitions
|
|
47
|
+
- [ ] Use hgroup for site title, page title, etc
|
|
48
|
+
- [ ] Images as `<figure>`
|
|
49
|
+
- [ ] Hidden routes
|
|
50
|
+
- [ ] Frontmatter settings
|
|
51
|
+
- [ ] HTML
|
|
52
|
+
- [ ] RSS
|
|
53
|
+
- [ ] Sitemap
|
|
54
|
+
- [ ] Heading anchors
|
|
55
|
+
- [ ] Taxonomy pages and smart frontmatter
|
|
56
|
+
- [ ] CSS cache busting
|
|
57
|
+
- [ ] Slogan in homepage title
|
|
58
|
+
|
|
59
|
+
### Medium priority
|
|
60
|
+
|
|
61
|
+
- [ ] Tests
|
|
62
|
+
- [ ] Break code into multiple files
|
|
63
|
+
- [ ] Image optimization (unpic)
|
|
64
|
+
- [ ] [SVG by mask](https://pqina.nl/blog/set-svg-background-image-fill-color/) and [CSS icons](https://antfu.me/posts/icons-in-pure-css)
|
|
65
|
+
- [ ] WYSIWYG editor
|
|
66
|
+
- [ ] Better signals
|
|
67
|
+
- [ ] File-written callback
|
|
68
|
+
- [ ] Themes
|
|
69
|
+
- [ ] Deploy
|
|
70
|
+
- [ ] Cloudflare pages
|
|
71
|
+
- [ ] GitHub pages
|
|
72
|
+
- [ ] Post-publish work (ping webmentions)
|
|
73
|
+
- [ ] [Desktop app](https://blackboard.sh/electrobun/docs/)
|
|
74
|
+
- [ ] Mermaid
|
|
75
|
+
- [ ] Codeblock syntax highlighting
|
|
76
|
+
- [ ] Extraction utilities (regex in archive)
|
|
77
|
+
- [ ] Smarter frontmatter
|
|
78
|
+
- [ ] Object dl
|
|
79
|
+
- [ ] Array ul
|
|
80
|
+
- [ ] Image
|
|
81
|
+
- [ ] URL
|
|
82
|
+
- [ ] Date
|
|
83
|
+
- [ ] TOC
|
|
84
|
+
- [ ] Versioning/publishing script
|
|
85
|
+
- [ ] Verify all element types form Obsidian
|
|
86
|
+
|
|
87
|
+
### Low priority
|
|
88
|
+
|
|
89
|
+
- [ ] Footnotes
|
|
90
|
+
- [ ] Frontmatter taxonomies
|
|
91
|
+
- [ ] Recursive frontmatter
|
|
92
|
+
- [ ] Browser search
|
|
93
|
+
- [ ] Pagination
|
|
94
|
+
- [ ] ATProto
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
> Why do I have a folder named ".vercel" in my project?
|
|
2
|
+
The ".vercel" folder is created when you link a directory to a Vercel project.
|
|
3
|
+
|
|
4
|
+
> What does the "project.json" file contain?
|
|
5
|
+
The "project.json" file contains:
|
|
6
|
+
- The ID of the Vercel project that you linked ("projectId")
|
|
7
|
+
- The ID of the user or team your Vercel project is owned by ("orgId")
|
|
8
|
+
|
|
9
|
+
> Should I commit the ".vercel" folder?
|
|
10
|
+
No, you should not share the ".vercel" folder with anyone.
|
|
11
|
+
Upon creation, it will be automatically added to your ".gitignore" file.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"projectId":"prj_4CUiAmqACnAFX1cjGqW5om7DbK4E","orgId":"team_CpFUbX79Msg6m5IOy2BzFxEE"}
|
package/getMetadata.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { XMLParser } from "fast-xml-parser"
|
|
2
|
+
import urlMetadata from 'url-metadata';
|
|
3
|
+
|
|
4
|
+
export default async function getMetadata(url) {
|
|
5
|
+
try {
|
|
6
|
+
const urlObject = new URL(url);
|
|
7
|
+
const allMetadata = await urlMetadata(urlObject.href, {
|
|
8
|
+
includeResponseBody: true,
|
|
9
|
+
ensureSecureImageRequest: true
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const parser = new XMLParser({
|
|
13
|
+
unpairedTags: ["!doctype", "meta", "link", "hr", "br", "img"],
|
|
14
|
+
ignoreAttributes: false,
|
|
15
|
+
stopNodes: ["*.pre", "*.script"],
|
|
16
|
+
processEntities: true,
|
|
17
|
+
htmlEntities: true
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
let parsedData = parser.parse(allMetadata.responseBody)
|
|
21
|
+
const webmentionEndpoint = parsedData?.html?.head?.link?.find(link => {
|
|
22
|
+
return link["@_rel"] === "webmention"
|
|
23
|
+
})?.["@_href"]
|
|
24
|
+
|
|
25
|
+
const metadata = {
|
|
26
|
+
image: allMetadata['og:image'],
|
|
27
|
+
ogURL: allMetadata['og:url'],
|
|
28
|
+
canonicalURL: allMetadata.canonical,
|
|
29
|
+
title: allMetadata.title,
|
|
30
|
+
ogTitle: allMetadata['og:title'],
|
|
31
|
+
author: allMetadata.author,
|
|
32
|
+
description: allMetadata.description,
|
|
33
|
+
webmentionEndpoint
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { metadata }
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.log({ fetchingMetadataError: error })
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
}
|
package/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import voot from "voot"
|
|
2
2
|
import { h } from 'hastscript'
|
|
3
3
|
import fs from "fs/promises"
|
|
4
4
|
import { readFileSync } from "fs"
|
|
5
5
|
import { rehype } from "rehype"
|
|
6
|
+
import { find } from 'unist-util-find'
|
|
6
7
|
import { toHast } from 'mdast-util-to-hast'
|
|
8
|
+
import { fromHtml } from 'hast-util-from-html'
|
|
7
9
|
import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
8
10
|
import { frontmatter } from "micromark-extension-frontmatter"
|
|
9
11
|
import { frontmatterFromMarkdown } from 'mdast-util-frontmatter'
|
|
10
12
|
import { gfmFootnoteFromMarkdown } from "mdast-util-gfm-footnote"
|
|
13
|
+
import { visit } from "unist-util-visit"
|
|
11
14
|
import { gfmFootnote } from "micromark-extension-gfm-footnote"
|
|
12
15
|
import { gfmStrikethroughFromMarkdown } from 'mdast-util-gfm-strikethrough'
|
|
13
16
|
import { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough'
|
|
@@ -16,7 +19,8 @@ import { gfmTableFromMarkdown } from 'mdast-util-gfm-table'
|
|
|
16
19
|
import { gfmTaskListItem } from 'micromark-extension-gfm-task-list-item' // TODO: Add
|
|
17
20
|
import { gfmTaskListItemFromMarkdown } from 'mdast-util-gfm-task-list-item' // TODO: Add
|
|
18
21
|
import { normalizeHeadings } from 'mdast-normalize-headings'
|
|
19
|
-
import { toString } from 'mdast-util-to-string'
|
|
22
|
+
import { toString as mdastToString } from 'mdast-util-to-string'
|
|
23
|
+
import { toString as hastToString } from 'hast-util-to-string'
|
|
20
24
|
import yaml from 'yaml'
|
|
21
25
|
import extractDate from "./extractDate.js"
|
|
22
26
|
import { testURL } from "./utils.js"
|
|
@@ -26,7 +30,7 @@ import { styleText } from "node:util"
|
|
|
26
30
|
const VOWEL_DIR = import.meta.dirname
|
|
27
31
|
|
|
28
32
|
|
|
29
|
-
/** @import {VotiveConfig, VotivePlugin, VotiveProcessor, ReadText, ReadAbstract, ReadFolder, ProcessorWrite, Router} from "votive" */
|
|
33
|
+
/** @import {Runner, ReadPath, VotiveConfig, VotivePlugin, VotiveProcessor, ReadText, ReadAbstract, ReadFolder, ProcessorWrite, Router} from "votive" */
|
|
30
34
|
|
|
31
35
|
/** @type {VotiveProcessor} */
|
|
32
36
|
const cssWriter = {
|
|
@@ -34,6 +38,19 @@ const cssWriter = {
|
|
|
34
38
|
write: writeCSS
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
/** @type {VotiveProcessor} */
|
|
42
|
+
const jpegLoader = {
|
|
43
|
+
syntax: "jpeg",
|
|
44
|
+
filter: {
|
|
45
|
+
extensions: [".jpeg", ".jpg"]
|
|
46
|
+
},
|
|
47
|
+
read: {
|
|
48
|
+
path: readImagePath,
|
|
49
|
+
},
|
|
50
|
+
write: writeImage
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
37
54
|
/** @type {ProcessorWrite} */
|
|
38
55
|
function writeCSS(destination, database, config) {
|
|
39
56
|
return {
|
|
@@ -42,28 +59,65 @@ function writeCSS(destination, database, config) {
|
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
61
|
|
|
62
|
+
function readURL(data) {
|
|
63
|
+
const hast = fromHtml(data)
|
|
64
|
+
const metadata = {}
|
|
65
|
+
|
|
66
|
+
visit(hast, (node) => {
|
|
67
|
+
if (node.tagName === "meta") {
|
|
68
|
+
if (node.properties && node.properties.property) {
|
|
69
|
+
metadata[node.properties.property] = node.properties.content
|
|
70
|
+
}
|
|
71
|
+
} else if (node.tagName === "title") {
|
|
72
|
+
metadata.title = hastToString(node)
|
|
73
|
+
} else if (node.tagName === "link") {
|
|
74
|
+
if (node.properties?.rel?.includes("me")) {
|
|
75
|
+
metadata.me = node.properties.href
|
|
76
|
+
} else if (node.properties?.rel?.includes("webmention")) {
|
|
77
|
+
metadata.webmention = node.properties.href
|
|
78
|
+
} else if (node.properties?.rel?.includes("icon")) {
|
|
79
|
+
metadata.icon = node.properties.href
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return metadata
|
|
85
|
+
}
|
|
86
|
+
|
|
45
87
|
/** @type {VotiveProcessor} */
|
|
46
88
|
const markdownReader = {
|
|
47
89
|
syntax: "mdast",
|
|
48
90
|
filter: { extensions: [".md"] },
|
|
49
91
|
read: {
|
|
50
92
|
text: readMarkdown,
|
|
93
|
+
url: readURL,
|
|
51
94
|
abstract: readAbstract,
|
|
52
95
|
folder: readFolder
|
|
53
96
|
},
|
|
54
97
|
write: writeHTML
|
|
55
98
|
}
|
|
56
99
|
|
|
57
|
-
|
|
100
|
+
|
|
101
|
+
async function removeCache() {
|
|
58
102
|
try {
|
|
59
|
-
await fs.rm("
|
|
60
|
-
console.info(styleText("yellow", "
|
|
103
|
+
await fs.rm("./.votive.db")
|
|
104
|
+
console.info(styleText("yellow", "Cache cleared"))
|
|
61
105
|
} catch (e) {
|
|
62
106
|
console.info(styleText("yellow", "No database cache found"))
|
|
63
107
|
}
|
|
64
108
|
}
|
|
65
109
|
|
|
66
|
-
|
|
110
|
+
async function removeDB() {
|
|
111
|
+
try {
|
|
112
|
+
await fs.rmdir("./output")
|
|
113
|
+
console.info(styleText("yellow", "Output cleared"))
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.info(styleText("yellow", "No output cache found"))
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await removeCache()
|
|
120
|
+
await removeDB()
|
|
67
121
|
|
|
68
122
|
/** @type {ReadText} */
|
|
69
123
|
function readMarkdown(string, filePath, destinationPath, database, config) {
|
|
@@ -89,6 +143,7 @@ function readMarkdown(string, filePath, destinationPath, database, config) {
|
|
|
89
143
|
metadata.inferred_label = pathInfo.name
|
|
90
144
|
const destinationInfo = path.parse(destinationPath)
|
|
91
145
|
metadata.prettyURL = (new URL(`${destinationInfo.dir}/${destinationInfo.name}`, "thismessage:/")).pathname
|
|
146
|
+
const jobs = []
|
|
92
147
|
|
|
93
148
|
selectMetadata(metadata)
|
|
94
149
|
|
|
@@ -103,8 +158,21 @@ function readMarkdown(string, filePath, destinationPath, database, config) {
|
|
|
103
158
|
}
|
|
104
159
|
}
|
|
105
160
|
|
|
161
|
+
visit(mdast, (node, index, parent) => {
|
|
162
|
+
if (node.type === "text" && parent.children.length === 1 && parent.type === "paragraph") {
|
|
163
|
+
const validURL = testURL(node.value)
|
|
164
|
+
if (validURL) {
|
|
165
|
+
|
|
166
|
+
jobs.push({
|
|
167
|
+
data: node.value,
|
|
168
|
+
runner: "text",
|
|
169
|
+
destination: destinationPath
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
})
|
|
106
174
|
|
|
107
|
-
return { abstract: mdast, metadata }
|
|
175
|
+
return { abstract: mdast, metadata, jobs }
|
|
108
176
|
}
|
|
109
177
|
|
|
110
178
|
/** @param {ReturnType<getMetadata>} metadata */
|
|
@@ -173,7 +241,7 @@ function getMetadata(tree) {
|
|
|
173
241
|
|
|
174
242
|
for (let i = 0; i < tree.children.length; i++) {
|
|
175
243
|
const child = tree.children[i]
|
|
176
|
-
const text =
|
|
244
|
+
const text = mdastToString(child)
|
|
177
245
|
switch (child.type) {
|
|
178
246
|
case "paragraph":
|
|
179
247
|
if (child.children.length !== 1) {
|
|
@@ -197,14 +265,14 @@ function getMetadata(tree) {
|
|
|
197
265
|
break
|
|
198
266
|
}
|
|
199
267
|
} else {
|
|
200
|
-
const inferred_date = extractDate(
|
|
268
|
+
const inferred_date = extractDate(mdastToString(child))
|
|
201
269
|
if (inferred_date) metadata.inferred_date = inferred_date
|
|
202
270
|
else i = Infinity
|
|
203
271
|
break
|
|
204
272
|
}
|
|
205
273
|
case "heading":
|
|
206
274
|
if (child.depth === 1) {
|
|
207
|
-
metadata.inferred_title =
|
|
275
|
+
metadata.inferred_title = mdastToString(child)
|
|
208
276
|
}
|
|
209
277
|
break
|
|
210
278
|
case "yaml":
|
|
@@ -261,15 +329,14 @@ function readFolder(folder, database, config, isRoot) {
|
|
|
261
329
|
})
|
|
262
330
|
}
|
|
263
331
|
|
|
332
|
+
|
|
264
333
|
if (isRoot) {
|
|
265
334
|
// TODO: Create theme job
|
|
266
335
|
const settings = database.getSettings(config.sourceFolder)
|
|
267
|
-
if (!settings.theme || settings.theme === "default") {
|
|
336
|
+
if (!settings.theme || settings.theme[0] === "default") {
|
|
268
337
|
database.setSetting(folder, "theme", "default")
|
|
269
338
|
// TODO: Save theme file (reset, typography, default)
|
|
270
339
|
|
|
271
|
-
console.log(process.cwd())
|
|
272
|
-
|
|
273
340
|
const resetStylesPath = path.join(VOWEL_DIR, "stylesheets", "ResetStyles.css")
|
|
274
341
|
const typeStylesPath = path.join(VOWEL_DIR, "stylesheets", "TypographyStyles.css")
|
|
275
342
|
const defaultStylesPath = path.join(VOWEL_DIR, "stylesheets", "DefaultStyles.css")
|
|
@@ -343,7 +410,7 @@ function writeHTML(destination, database, config) {
|
|
|
343
410
|
|
|
344
411
|
const treeStyleSheets = []
|
|
345
412
|
|
|
346
|
-
!settings.theme && treeStyleSheets.push(
|
|
413
|
+
!settings.theme || settings.theme.includes("default") && treeStyleSheets.push(
|
|
347
414
|
h('link', {
|
|
348
415
|
rel: "stylesheet",
|
|
349
416
|
href: "/default.css"
|
|
@@ -455,6 +522,24 @@ function writeHTML(destination, database, config) {
|
|
|
455
522
|
|
|
456
523
|
const treeMain = h('main.h-entry', toHast(abstract).children)
|
|
457
524
|
|
|
525
|
+
visit(treeMain, (node, index, parent) => {
|
|
526
|
+
if (node.type === "text" && parent.tagName === 'p' && parent.children.length === 1) {
|
|
527
|
+
const validURL = testURL(node.value)
|
|
528
|
+
if (validURL) {
|
|
529
|
+
const metadata = database.getURL(node.value)
|
|
530
|
+
if (metadata) {
|
|
531
|
+
parent.tagName = "article"
|
|
532
|
+
// TODO: Add url to metadata
|
|
533
|
+
parent.children = [
|
|
534
|
+
h("a", { href: node.value },
|
|
535
|
+
h("h2", metadata.title)
|
|
536
|
+
)
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
})
|
|
542
|
+
|
|
458
543
|
// TODO: Write sidebar
|
|
459
544
|
const treeSidebar = h('nav.secondary')
|
|
460
545
|
const treeFooter = h('footer', `© ${new Date().getFullYear()}`)
|
|
@@ -522,32 +607,63 @@ function router({ name, dir, inRootDir }) {
|
|
|
522
607
|
}
|
|
523
608
|
}
|
|
524
609
|
|
|
610
|
+
/** @type {ReadPath} */
|
|
611
|
+
async function readImagePath(string, database) {
|
|
612
|
+
// TODO: Resize and optimize images
|
|
613
|
+
|
|
614
|
+
return {
|
|
615
|
+
metadata: {},
|
|
616
|
+
abstract: { path: string }
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** @type {ProcessorWrite} */
|
|
621
|
+
async function writeImage(destination, database, config) {
|
|
622
|
+
const buffer = await fs.readFile(destination.path)
|
|
623
|
+
return {
|
|
624
|
+
buffer
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
525
628
|
/** @type {VotivePlugin} */
|
|
526
|
-
const
|
|
629
|
+
const vowelMarkdown = {
|
|
527
630
|
name: "vowel",
|
|
528
631
|
processors: [markdownReader, cssWriter],
|
|
529
|
-
runners: {},
|
|
530
632
|
router
|
|
531
633
|
}
|
|
532
634
|
|
|
635
|
+
/** @type {VotivePlugin} */
|
|
636
|
+
const vowelJpeg = {
|
|
637
|
+
name: "vowel-jpeg",
|
|
638
|
+
processors: [jpegLoader],
|
|
639
|
+
router: ({ name, dir, ext }) => {
|
|
640
|
+
return {
|
|
641
|
+
name, dir, ext
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
|
|
533
648
|
/** @type {VotiveConfig} */
|
|
534
649
|
const config = {
|
|
535
|
-
sourceFolder: "
|
|
650
|
+
sourceFolder: ".",
|
|
536
651
|
destinationFolder: "output",
|
|
537
652
|
plugins: [
|
|
538
|
-
|
|
653
|
+
vowelMarkdown,
|
|
654
|
+
vowelJpeg
|
|
539
655
|
]
|
|
540
656
|
}
|
|
541
657
|
|
|
542
|
-
const then = performance.now()
|
|
543
658
|
|
|
544
659
|
async function init() {
|
|
545
|
-
|
|
546
|
-
await fs.
|
|
547
|
-
|
|
660
|
+
const then = performance.now()
|
|
661
|
+
// await fs.rm(config.destinationFolder, { recursive: true, force: true })
|
|
662
|
+
// await fs.mkdir(config.destinationFolder, { recursive: true })
|
|
663
|
+
const cache = await voot(config)
|
|
664
|
+
console.log(styleText("red", (performance.now() - then).toFixed(4) + "ms"))
|
|
548
665
|
}
|
|
549
666
|
|
|
550
|
-
console.log(styleText("red", (performance.now() - then).toFixed() + "ms"))
|
|
551
667
|
|
|
552
668
|
export default init
|
|
553
669
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vowel",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"bin": "bin.js",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"scripts": {
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"description": "Markdown websites",
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"fast-xml-parser": "^5.3.6",
|
|
19
20
|
"faye-websocket": "^0.11.4",
|
|
21
|
+
"hast-util-from-html": "^2.0.3",
|
|
22
|
+
"hast-util-to-string": "^3.0.1",
|
|
20
23
|
"hastscript": "^9.0.1",
|
|
21
24
|
"js-yaml": "^4.1.0",
|
|
22
25
|
"mdast": "^2.3.2",
|
|
@@ -42,11 +45,14 @@
|
|
|
42
45
|
"remark-parse": "^11.0.0",
|
|
43
46
|
"remark-rehype": "^11.1.2",
|
|
44
47
|
"unified": "^11.0.5",
|
|
48
|
+
"unist-util-find": "^3.0.0",
|
|
45
49
|
"unist-util-is": "^6.0.0",
|
|
46
50
|
"unist-util-position": "5.0.0",
|
|
47
51
|
"unist-util-stringify-position": "^4.0.0",
|
|
48
52
|
"unist-util-visit": "^5.0.0",
|
|
49
53
|
"unist-util-visit-parents": "^6.0.0",
|
|
54
|
+
"url-metadata": "^5.4.1",
|
|
55
|
+
"voot": "^0.0.2",
|
|
50
56
|
"votive": "^0.0.6",
|
|
51
57
|
"xast-util-sitemap": "^2.0.0",
|
|
52
58
|
"xast-util-to-xml": "^4.0.0",
|
package/regex.js
CHANGED
package/utils.js
CHANGED
package/books/.votive.db
DELETED
|
Binary file
|