vowel 0.6.3 → 0.6.4
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 +22 -16
- package/bin.js +41 -4
- package/config.js +28 -9
- package/package.json +5 -1
- package/plugins/deploy-cloudflare/index.js +78 -0
- package/plugins/fonts/index.js +2 -2
- package/plugins/html/image.js +78 -0
- package/plugins/html/index.js +788 -0
- package/plugins/images/index.js +5 -5
- package/plugins/markdown/index.js +141 -1070
- package/plugins/markdown/metadata.js +147 -0
- package/plugins/markdown/robots.js +18 -0
- package/plugins/styles/index.js +6 -14
- package/plugins/urls/index.js +91 -0
- package/plugins/vectors/index.js +1 -1
- package/plugins/xml/index.js +25 -14
- package/stylesheets/SyntaxHighlightingStyles.css +237 -0
- package/utils.js +2 -2
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { toString as mdastToString } from 'mdast-util-to-string'
|
|
2
|
+
import extractDate from "./../../extractDate.js"
|
|
3
|
+
import { testURL, toTitleCase } from "./../../utils.js"
|
|
4
|
+
import yaml from 'yaml'
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
- menu_item: true | false
|
|
10
|
+
- secret_url: true | false
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} tree
|
|
15
|
+
* @param {string} filePath
|
|
16
|
+
* @param {string} targetPath
|
|
17
|
+
*/
|
|
18
|
+
function getMetadata(tree, filePath, targetPath) {
|
|
19
|
+
const metadata = {}
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < tree.children.length; i++) {
|
|
22
|
+
const child = tree.children[i]
|
|
23
|
+
const text = mdastToString(child)
|
|
24
|
+
switch (child.type) {
|
|
25
|
+
case "paragraph":
|
|
26
|
+
if (child.children.length !== 1) {
|
|
27
|
+
if (!metadata.fm_description && !metadata.inferred_description) {
|
|
28
|
+
metadata.inferred_description = text
|
|
29
|
+
}
|
|
30
|
+
i = Infinity
|
|
31
|
+
break
|
|
32
|
+
} else if (child.children[0].type === "image") {
|
|
33
|
+
metadata.inferred_image = child.children[0].url
|
|
34
|
+
metadata.inferred_alt_text = child.children[0].alt
|
|
35
|
+
break
|
|
36
|
+
} else if (testURL(text)) {
|
|
37
|
+
const url = new URL(text)
|
|
38
|
+
if (text.match(/\.(jpeg|jpg|png)$/)) {
|
|
39
|
+
metadata.inferred_image = url
|
|
40
|
+
break
|
|
41
|
+
} else {
|
|
42
|
+
metadata.inferred_link = url
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const inferred_date = extractDate(mdastToString(child))
|
|
47
|
+
if (inferred_date) {
|
|
48
|
+
metadata.inferred_date = inferred_date
|
|
49
|
+
} else {
|
|
50
|
+
if (!metadata.fm_description && !metadata.inferred_description) {
|
|
51
|
+
metadata.inferred_description = text
|
|
52
|
+
}
|
|
53
|
+
// tree.children.splice(0, i + 1)
|
|
54
|
+
i = Infinity
|
|
55
|
+
}
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
case "heading":
|
|
59
|
+
if (child.depth === 1) {
|
|
60
|
+
metadata.inferred_title = mdastToString(child) || toTitleCase(path.parse(filePath).name)
|
|
61
|
+
tree.children.splice(0, i + 1)
|
|
62
|
+
} else {
|
|
63
|
+
i = Infinity;
|
|
64
|
+
}
|
|
65
|
+
break
|
|
66
|
+
case "yaml":
|
|
67
|
+
const frontmatter = yaml.parse(child.value)
|
|
68
|
+
for (const key in frontmatter) {
|
|
69
|
+
const reservedProperties = ["rss_item", "sitemap_item", "html_file", "global_menu_item", "local_menu_item", "secret_key", "theme", "logo", "wordmark", "breadcrumb"]
|
|
70
|
+
if (reservedProperties.includes(key)) {
|
|
71
|
+
metadata[key] = frontmatter[key]
|
|
72
|
+
} else {
|
|
73
|
+
metadata["fm_" + key] = frontmatter[key]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
default:
|
|
78
|
+
i = Infinity
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
const pathInfo = path.parse(filePath)
|
|
85
|
+
|
|
86
|
+
metadata.inferred_label = toTitleCase(pathInfo.name)
|
|
87
|
+
|
|
88
|
+
if (targetPath) {
|
|
89
|
+
const targetInfo = path.parse(targetPath)
|
|
90
|
+
const name = targetInfo.name === "index" ? "" : targetInfo.name
|
|
91
|
+
// FIXME the prettyURL should include the preceding slash
|
|
92
|
+
metadata.prettyURL = (new URL(`${targetInfo.dir}/${name}`, "thismessage:/")).pathname
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
selectMetadata(metadata)
|
|
96
|
+
|
|
97
|
+
return metadata
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
function selectMetadata(metadata) {
|
|
103
|
+
const date =
|
|
104
|
+
metadata.fm_date
|
|
105
|
+
|| metadata.inferred_date
|
|
106
|
+
|
|
107
|
+
if (date) {
|
|
108
|
+
metadata.date = date
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const title =
|
|
112
|
+
metadata.fm_title
|
|
113
|
+
|| metadata.inferred_title
|
|
114
|
+
|| metadata.inferred_label
|
|
115
|
+
|
|
116
|
+
if (title) {
|
|
117
|
+
metadata.title = title
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const breadcrumb =
|
|
121
|
+
metadata.fm_breadcrumb
|
|
122
|
+
|| metadata.title
|
|
123
|
+
|| metadata.inferred_label
|
|
124
|
+
|
|
125
|
+
if (breadcrumb) {
|
|
126
|
+
metadata.breadcrumb = breadcrumb
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const description =
|
|
130
|
+
metadata.fm_description
|
|
131
|
+
|| metadata.tagline
|
|
132
|
+
|| metadata.inferred_description
|
|
133
|
+
|
|
134
|
+
if (description) {
|
|
135
|
+
metadata.description = description
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const image =
|
|
139
|
+
metadata.fm_image
|
|
140
|
+
|| metadata.inferred_image
|
|
141
|
+
|
|
142
|
+
if (image) {
|
|
143
|
+
metadata.image = image
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default getMetadata
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function generateRobots() {
|
|
2
|
+
return `User-agent: Google-Extended
|
|
3
|
+
Allow: /
|
|
4
|
+
|
|
5
|
+
User-agent: Googlebot-Image
|
|
6
|
+
Disallow: /
|
|
7
|
+
|
|
8
|
+
User-agent: GPTBot
|
|
9
|
+
Disallow: /
|
|
10
|
+
|
|
11
|
+
User-agent: ChatGPT-User
|
|
12
|
+
Disallow: /
|
|
13
|
+
|
|
14
|
+
User-agent: CCBot
|
|
15
|
+
Disallow: /`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default generateRobots
|
package/plugins/styles/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
1
|
import { transform } from "lightningcss"
|
|
3
2
|
|
|
4
3
|
/** @import * as Votive from "votive" */
|
|
5
4
|
|
|
6
5
|
/** @type {Votive.ProcessorWrite} */
|
|
7
|
-
function writeCSS(
|
|
6
|
+
function writeCSS(target, settings, api, config) {
|
|
8
7
|
const { code, map } = transform({
|
|
9
|
-
filename:
|
|
10
|
-
code: Buffer.from(
|
|
8
|
+
filename: target.path,
|
|
9
|
+
code: Buffer.from(target.abstract.css),
|
|
11
10
|
minify: true,
|
|
12
11
|
targets: {
|
|
13
12
|
chrome: 140 << 16,
|
|
@@ -27,15 +26,7 @@ function writeCSS(destination, database, config) {
|
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
/** @type {Votive.ReadText} */
|
|
30
|
-
function readCSS(text, filePath,
|
|
31
|
-
const pathInfo = path.parse(filePath)
|
|
32
|
-
|
|
33
|
-
database.setting.create(
|
|
34
|
-
pathInfo.dir,
|
|
35
|
-
"stylesheets",
|
|
36
|
-
filePath
|
|
37
|
-
)
|
|
38
|
-
|
|
29
|
+
function readCSS(text, filePath, targetPath, api, config) {
|
|
39
30
|
const metadata = {}
|
|
40
31
|
const abstract = {
|
|
41
32
|
css: text
|
|
@@ -43,7 +34,8 @@ function readCSS(text, filePath, destinationPath, database, config) {
|
|
|
43
34
|
|
|
44
35
|
return {
|
|
45
36
|
metadata,
|
|
46
|
-
abstract
|
|
37
|
+
abstract,
|
|
38
|
+
settings: { stylesheets: [filePath] }
|
|
47
39
|
}
|
|
48
40
|
}
|
|
49
41
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { parseURLMetadata } from "votive/internals"
|
|
2
|
+
|
|
3
|
+
/** @import * as Votive from "votive" */
|
|
4
|
+
|
|
5
|
+
const EXTERNAL_LINK_RE = /^https?:\/\/\S+$/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Matches a markdown paragraph whose only content is a bare URL - see
|
|
9
|
+
* CLAUDE.md: "a URL that is the only child of a paragraph" -
|
|
10
|
+
* Dinosaurs lived 65,000,000 years ago.
|
|
11
|
+
*
|
|
12
|
+
* https://dinosaurs.com/timeline
|
|
13
|
+
*
|
|
14
|
+
* We still find their fossils today.
|
|
15
|
+
* Same shape html/index.js's own `testPaths` already matches for
|
|
16
|
+
* internal single-link paragraphs (one text-only child) - this is the
|
|
17
|
+
* external-URL counterpart.
|
|
18
|
+
* @param {any} node
|
|
19
|
+
*/
|
|
20
|
+
function isExternalLinkParagraph(node) {
|
|
21
|
+
if (node.type !== "element") return false
|
|
22
|
+
if (node.tagName !== "p") return false
|
|
23
|
+
if (node.children.length !== 1) return false
|
|
24
|
+
const [child] = node.children
|
|
25
|
+
if (!child || typeof child.value !== "string") return false
|
|
26
|
+
return EXTERNAL_LINK_RE.test(child.value)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Walks a target's abstract for link-preview paragraphs and queues one
|
|
31
|
+
* fetch task per URL found, so the write side (html/index.js) can render
|
|
32
|
+
* a preview card once the data is cached. Deliberately does not fetch
|
|
33
|
+
* anything itself - tasks are only ever run when the caller invokes
|
|
34
|
+
* runFetches() (see votive's fetchURLs.js), and a URL already cached
|
|
35
|
+
* (success or in a failure cooldown) is skipped automatically, so this
|
|
36
|
+
* never re-fetches the same link on every build.
|
|
37
|
+
* @type {Votive.ReadAbstract}
|
|
38
|
+
*/
|
|
39
|
+
function transformFile(abstract, settings, api, config, targetFilePath) {
|
|
40
|
+
const urls = []
|
|
41
|
+
|
|
42
|
+
function walk(node) {
|
|
43
|
+
if (!node || typeof node !== "object") return
|
|
44
|
+
if (isExternalLinkParagraph(node)) {
|
|
45
|
+
urls.push({
|
|
46
|
+
data: node.children[0].value,
|
|
47
|
+
runner: "text",
|
|
48
|
+
target: targetFilePath
|
|
49
|
+
})
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(node.children)) node.children.forEach(walk)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
walk(abstract)
|
|
56
|
+
|
|
57
|
+
return { abstract, urls }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parses a fetched page into the flat shape link-preview rendering
|
|
62
|
+
* wants, preferring OpenGraph, then standard meta tags, then Twitter
|
|
63
|
+
* Card, for each field independently.
|
|
64
|
+
* @param {string} html
|
|
65
|
+
* @returns {{ title: string, description: string, image: string | undefined }}
|
|
66
|
+
*/
|
|
67
|
+
function parseLinkPreview(html) {
|
|
68
|
+
const meta = parseURLMetadata(html)
|
|
69
|
+
return {
|
|
70
|
+
title: meta.openGraph.title || meta.title || meta.twitterCard.title || "",
|
|
71
|
+
description: meta.openGraph.description || meta.description || meta.twitterCard.description || "",
|
|
72
|
+
image: meta.openGraph.image || meta.twitterCard.image || undefined
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @type {Votive.VotiveProcessor} */
|
|
77
|
+
const linkPreviewProcessor = {
|
|
78
|
+
extensions: [".html"],
|
|
79
|
+
format: "text",
|
|
80
|
+
transformFile,
|
|
81
|
+
readURL: parseLinkPreview
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @type {Votive.VotivePlugin} */
|
|
85
|
+
const vowelURLsPlugin = {
|
|
86
|
+
name: "vowel-urls",
|
|
87
|
+
processors: [linkPreviewProcessor]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default vowelURLsPlugin
|
|
91
|
+
export { isExternalLinkParagraph, parseLinkPreview }
|
package/plugins/vectors/index.js
CHANGED
|
@@ -14,7 +14,7 @@ async function writeFile(target) {
|
|
|
14
14
|
const processor = {
|
|
15
15
|
extensions: [".svg"],
|
|
16
16
|
format: "text",
|
|
17
|
-
readFile: (text, filePath,
|
|
17
|
+
readFile: (text, filePath, targetPath, api) => {
|
|
18
18
|
const monochrome = text.includes("currentColor") || Boolean(text.match(/#000\b/))
|
|
19
19
|
return {
|
|
20
20
|
abstract: { svg: text },
|
package/plugins/xml/index.js
CHANGED
|
@@ -7,18 +7,23 @@ import xml from "xml"
|
|
|
7
7
|
const processor = {
|
|
8
8
|
extensions: [".xml"],
|
|
9
9
|
format: "text",
|
|
10
|
-
writeFile: (target,
|
|
10
|
+
writeFile: (target, settings, api) => {
|
|
11
11
|
if (target.path === "sitemap.xml") {
|
|
12
|
-
|
|
13
12
|
// FIXME move the filter to SQLite
|
|
14
|
-
const pages =
|
|
15
|
-
.
|
|
13
|
+
const pages = api
|
|
14
|
+
.targets({
|
|
16
15
|
folder: "",
|
|
17
16
|
recursive: true,
|
|
18
|
-
query: {
|
|
19
|
-
|
|
17
|
+
query: {
|
|
18
|
+
"!": {
|
|
19
|
+
"|": {
|
|
20
|
+
"sitemap_item": false,
|
|
21
|
+
"html_file": false
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
20
25
|
})
|
|
21
|
-
.filter(a => a.
|
|
26
|
+
.filter(a => a.extension === ".html" && a.path)
|
|
22
27
|
|
|
23
28
|
const domain = target.metadata.domain
|
|
24
29
|
&& target.metadata.domain.startsWith("http")
|
|
@@ -58,19 +63,22 @@ const processor = {
|
|
|
58
63
|
|
|
59
64
|
return {
|
|
60
65
|
data: sitemap,
|
|
61
|
-
|
|
66
|
+
urls: []
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
} else if (target.path === "feed.xml") {
|
|
65
70
|
|
|
66
|
-
const pages =
|
|
67
|
-
query: {
|
|
71
|
+
const pages = api.targets({
|
|
72
|
+
query: {
|
|
73
|
+
"!": {
|
|
74
|
+
rss_item: false
|
|
75
|
+
}
|
|
76
|
+
},
|
|
68
77
|
folder: "",
|
|
69
78
|
recursive: true,
|
|
70
|
-
|
|
79
|
+
orderBy: { property: "date", direction: "desc" }
|
|
71
80
|
})
|
|
72
81
|
.filter(a => a.metadata.date)
|
|
73
|
-
.sort((a, b) => b.metadata.date - a.metadata.date)
|
|
74
82
|
|
|
75
83
|
const domain = target.metadata.domain
|
|
76
84
|
&& target.metadata.domain.startsWith("http")
|
|
@@ -87,7 +95,7 @@ const processor = {
|
|
|
87
95
|
},
|
|
88
96
|
{
|
|
89
97
|
title: target.metadata.title
|
|
90
|
-
}
|
|
98
|
+
},
|
|
91
99
|
)
|
|
92
100
|
|
|
93
101
|
|
|
@@ -119,7 +127,6 @@ const processor = {
|
|
|
119
127
|
...pages.map((page) => {
|
|
120
128
|
const url = (new URL(page.path, domain)).href
|
|
121
129
|
|
|
122
|
-
|
|
123
130
|
/** @type {object[]} */
|
|
124
131
|
const entry = [
|
|
125
132
|
{
|
|
@@ -151,6 +158,10 @@ const processor = {
|
|
|
151
158
|
});
|
|
152
159
|
}
|
|
153
160
|
|
|
161
|
+
entry.push({
|
|
162
|
+
content: page.data
|
|
163
|
+
})
|
|
164
|
+
|
|
154
165
|
// TODO consider rendering full page in RSS
|
|
155
166
|
|
|
156
167
|
/*
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Syntax highlighting theme for code blocks (rehype-highlight), based on
|
|
3
|
+
* highlight.js's GitHub theme. Light by default, dark under
|
|
4
|
+
* prefers-color-scheme - see highlight.js/styles/github.css and
|
|
5
|
+
* github-dark.css for the source themes this was combined from.
|
|
6
|
+
*/
|
|
7
|
+
pre code.hljs {
|
|
8
|
+
display: block;
|
|
9
|
+
overflow-x: auto;
|
|
10
|
+
padding: 1em
|
|
11
|
+
}
|
|
12
|
+
code.hljs {
|
|
13
|
+
padding: 3px 5px
|
|
14
|
+
}
|
|
15
|
+
/*!
|
|
16
|
+
Theme: GitHub
|
|
17
|
+
Description: Light theme as seen on github.com
|
|
18
|
+
Author: github.com
|
|
19
|
+
Maintainer: @Hirse
|
|
20
|
+
Updated: 2021-05-15
|
|
21
|
+
|
|
22
|
+
Outdated base version: https://github.com/primer/github-syntax-light
|
|
23
|
+
Current colors taken from GitHub's CSS
|
|
24
|
+
*/
|
|
25
|
+
.hljs {
|
|
26
|
+
color: #24292e;
|
|
27
|
+
background: #ffffff
|
|
28
|
+
}
|
|
29
|
+
.hljs-doctag,
|
|
30
|
+
.hljs-keyword,
|
|
31
|
+
.hljs-meta .hljs-keyword,
|
|
32
|
+
.hljs-template-tag,
|
|
33
|
+
.hljs-template-variable,
|
|
34
|
+
.hljs-type,
|
|
35
|
+
.hljs-variable.language_ {
|
|
36
|
+
/* prettylights-syntax-keyword */
|
|
37
|
+
color: #d73a49
|
|
38
|
+
}
|
|
39
|
+
.hljs-title,
|
|
40
|
+
.hljs-title.class_,
|
|
41
|
+
.hljs-title.class_.inherited__,
|
|
42
|
+
.hljs-title.function_ {
|
|
43
|
+
/* prettylights-syntax-entity */
|
|
44
|
+
color: #6f42c1
|
|
45
|
+
}
|
|
46
|
+
.hljs-attr,
|
|
47
|
+
.hljs-attribute,
|
|
48
|
+
.hljs-literal,
|
|
49
|
+
.hljs-meta,
|
|
50
|
+
.hljs-number,
|
|
51
|
+
.hljs-operator,
|
|
52
|
+
.hljs-variable,
|
|
53
|
+
.hljs-selector-attr,
|
|
54
|
+
.hljs-selector-class,
|
|
55
|
+
.hljs-selector-id {
|
|
56
|
+
/* prettylights-syntax-constant */
|
|
57
|
+
color: #005cc5
|
|
58
|
+
}
|
|
59
|
+
.hljs-regexp,
|
|
60
|
+
.hljs-string,
|
|
61
|
+
.hljs-meta .hljs-string {
|
|
62
|
+
/* prettylights-syntax-string */
|
|
63
|
+
color: #032f62
|
|
64
|
+
}
|
|
65
|
+
.hljs-built_in,
|
|
66
|
+
.hljs-symbol {
|
|
67
|
+
/* prettylights-syntax-variable */
|
|
68
|
+
color: #e36209
|
|
69
|
+
}
|
|
70
|
+
.hljs-comment,
|
|
71
|
+
.hljs-code,
|
|
72
|
+
.hljs-formula {
|
|
73
|
+
/* prettylights-syntax-comment */
|
|
74
|
+
color: #6a737d
|
|
75
|
+
}
|
|
76
|
+
.hljs-name,
|
|
77
|
+
.hljs-quote,
|
|
78
|
+
.hljs-selector-tag,
|
|
79
|
+
.hljs-selector-pseudo {
|
|
80
|
+
/* prettylights-syntax-entity-tag */
|
|
81
|
+
color: #22863a
|
|
82
|
+
}
|
|
83
|
+
.hljs-subst {
|
|
84
|
+
/* prettylights-syntax-storage-modifier-import */
|
|
85
|
+
color: #24292e
|
|
86
|
+
}
|
|
87
|
+
.hljs-section {
|
|
88
|
+
/* prettylights-syntax-markup-heading */
|
|
89
|
+
color: #005cc5;
|
|
90
|
+
font-weight: bold
|
|
91
|
+
}
|
|
92
|
+
.hljs-bullet {
|
|
93
|
+
/* prettylights-syntax-markup-list */
|
|
94
|
+
color: #735c0f
|
|
95
|
+
}
|
|
96
|
+
.hljs-emphasis {
|
|
97
|
+
/* prettylights-syntax-markup-italic */
|
|
98
|
+
color: #24292e;
|
|
99
|
+
font-style: italic
|
|
100
|
+
}
|
|
101
|
+
.hljs-strong {
|
|
102
|
+
/* prettylights-syntax-markup-bold */
|
|
103
|
+
color: #24292e;
|
|
104
|
+
font-weight: bold
|
|
105
|
+
}
|
|
106
|
+
.hljs-addition {
|
|
107
|
+
/* prettylights-syntax-markup-inserted */
|
|
108
|
+
color: #22863a;
|
|
109
|
+
background-color: #f0fff4
|
|
110
|
+
}
|
|
111
|
+
.hljs-deletion {
|
|
112
|
+
/* prettylights-syntax-markup-deleted */
|
|
113
|
+
color: #b31d28;
|
|
114
|
+
background-color: #ffeef0
|
|
115
|
+
}
|
|
116
|
+
.hljs-char.escape_,
|
|
117
|
+
.hljs-link,
|
|
118
|
+
.hljs-params,
|
|
119
|
+
.hljs-property,
|
|
120
|
+
.hljs-punctuation,
|
|
121
|
+
.hljs-tag {
|
|
122
|
+
/* purposely ignored */
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@media (prefers-color-scheme: dark) {
|
|
127
|
+
/*!
|
|
128
|
+
Theme: GitHub Dark
|
|
129
|
+
Description: Dark theme as seen on github.com
|
|
130
|
+
Author: github.com
|
|
131
|
+
Maintainer: @Hirse
|
|
132
|
+
Updated: 2021-05-15
|
|
133
|
+
|
|
134
|
+
Outdated base version: https://github.com/primer/github-syntax-dark
|
|
135
|
+
Current colors taken from GitHub's CSS
|
|
136
|
+
*/
|
|
137
|
+
.hljs {
|
|
138
|
+
color: #c9d1d9;
|
|
139
|
+
background: #0d1117
|
|
140
|
+
}
|
|
141
|
+
.hljs-doctag,
|
|
142
|
+
.hljs-keyword,
|
|
143
|
+
.hljs-meta .hljs-keyword,
|
|
144
|
+
.hljs-template-tag,
|
|
145
|
+
.hljs-template-variable,
|
|
146
|
+
.hljs-type,
|
|
147
|
+
.hljs-variable.language_ {
|
|
148
|
+
/* prettylights-syntax-keyword */
|
|
149
|
+
color: #ff7b72
|
|
150
|
+
}
|
|
151
|
+
.hljs-title,
|
|
152
|
+
.hljs-title.class_,
|
|
153
|
+
.hljs-title.class_.inherited__,
|
|
154
|
+
.hljs-title.function_ {
|
|
155
|
+
/* prettylights-syntax-entity */
|
|
156
|
+
color: #d2a8ff
|
|
157
|
+
}
|
|
158
|
+
.hljs-attr,
|
|
159
|
+
.hljs-attribute,
|
|
160
|
+
.hljs-literal,
|
|
161
|
+
.hljs-meta,
|
|
162
|
+
.hljs-number,
|
|
163
|
+
.hljs-operator,
|
|
164
|
+
.hljs-variable,
|
|
165
|
+
.hljs-selector-attr,
|
|
166
|
+
.hljs-selector-class,
|
|
167
|
+
.hljs-selector-id {
|
|
168
|
+
/* prettylights-syntax-constant */
|
|
169
|
+
color: #79c0ff
|
|
170
|
+
}
|
|
171
|
+
.hljs-regexp,
|
|
172
|
+
.hljs-string,
|
|
173
|
+
.hljs-meta .hljs-string {
|
|
174
|
+
/* prettylights-syntax-string */
|
|
175
|
+
color: #a5d6ff
|
|
176
|
+
}
|
|
177
|
+
.hljs-built_in,
|
|
178
|
+
.hljs-symbol {
|
|
179
|
+
/* prettylights-syntax-variable */
|
|
180
|
+
color: #ffa657
|
|
181
|
+
}
|
|
182
|
+
.hljs-comment,
|
|
183
|
+
.hljs-code,
|
|
184
|
+
.hljs-formula {
|
|
185
|
+
/* prettylights-syntax-comment */
|
|
186
|
+
color: #8b949e
|
|
187
|
+
}
|
|
188
|
+
.hljs-name,
|
|
189
|
+
.hljs-quote,
|
|
190
|
+
.hljs-selector-tag,
|
|
191
|
+
.hljs-selector-pseudo {
|
|
192
|
+
/* prettylights-syntax-entity-tag */
|
|
193
|
+
color: #7ee787
|
|
194
|
+
}
|
|
195
|
+
.hljs-subst {
|
|
196
|
+
/* prettylights-syntax-storage-modifier-import */
|
|
197
|
+
color: #c9d1d9
|
|
198
|
+
}
|
|
199
|
+
.hljs-section {
|
|
200
|
+
/* prettylights-syntax-markup-heading */
|
|
201
|
+
color: #1f6feb;
|
|
202
|
+
font-weight: bold
|
|
203
|
+
}
|
|
204
|
+
.hljs-bullet {
|
|
205
|
+
/* prettylights-syntax-markup-list */
|
|
206
|
+
color: #f2cc60
|
|
207
|
+
}
|
|
208
|
+
.hljs-emphasis {
|
|
209
|
+
/* prettylights-syntax-markup-italic */
|
|
210
|
+
color: #c9d1d9;
|
|
211
|
+
font-style: italic
|
|
212
|
+
}
|
|
213
|
+
.hljs-strong {
|
|
214
|
+
/* prettylights-syntax-markup-bold */
|
|
215
|
+
color: #c9d1d9;
|
|
216
|
+
font-weight: bold
|
|
217
|
+
}
|
|
218
|
+
.hljs-addition {
|
|
219
|
+
/* prettylights-syntax-markup-inserted */
|
|
220
|
+
color: #aff5b4;
|
|
221
|
+
background-color: #033a16
|
|
222
|
+
}
|
|
223
|
+
.hljs-deletion {
|
|
224
|
+
/* prettylights-syntax-markup-deleted */
|
|
225
|
+
color: #ffdcd7;
|
|
226
|
+
background-color: #67060c
|
|
227
|
+
}
|
|
228
|
+
.hljs-char.escape_,
|
|
229
|
+
.hljs-link,
|
|
230
|
+
.hljs-params,
|
|
231
|
+
.hljs-property,
|
|
232
|
+
.hljs-punctuation,
|
|
233
|
+
.hljs-tag {
|
|
234
|
+
/* purposely ignored */
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
}
|
package/utils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {fromMarkdown} from 'mdast-util-from-markdown'
|
|
2
2
|
import {toHast} from 'mdast-util-to-hast'
|
|
3
|
-
import {toHtml} from 'hast-util-to-html'
|
|
4
3
|
import path from "node:path"
|
|
5
4
|
import { randomUUID } from "node:crypto"
|
|
6
5
|
|
|
@@ -75,6 +74,7 @@ export function toTitleCase(string) {
|
|
|
75
74
|
export function createHashtagPage(tag) {
|
|
76
75
|
const markdown = `# ${toTitleCase(tag)}\n\n/**?tag=${tag}`
|
|
77
76
|
const mdast = fromMarkdown(markdown)
|
|
77
|
+
const hast = toHast(mdast)
|
|
78
78
|
|
|
79
|
-
return
|
|
79
|
+
return hast
|
|
80
80
|
}
|