vowel 0.5.0 → 0.6.1
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/package.json +3 -1
- package/plugins/images/index.js +31 -1
- package/plugins/markdown/index.js +99 -17
- package/utils.js +40 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vowel",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"bin": "bin.js",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"scripts": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"remark-html": "^16.0.1",
|
|
59
59
|
"remark-parse": "^11.0.0",
|
|
60
60
|
"remark-rehype": "^11.1.2",
|
|
61
|
+
"sharp": "^0.35.3",
|
|
61
62
|
"unified": "^11.0.5",
|
|
62
63
|
"unist-util-find": "^3.0.0",
|
|
63
64
|
"unist-util-is": "^6.0.0",
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"unist-util-stringify-position": "^4.0.0",
|
|
67
68
|
"unist-util-visit": "^5.0.0",
|
|
68
69
|
"unist-util-visit-parents": "^6.0.0",
|
|
70
|
+
"unpic": "^4.2.2",
|
|
69
71
|
"url-metadata": "^5.4.1",
|
|
70
72
|
"voot": "^0.1.0",
|
|
71
73
|
"votive": "^0.4.0",
|
package/plugins/images/index.js
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto"
|
|
1
2
|
import fs from "fs/promises"
|
|
3
|
+
import sharp from 'sharp'
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import {createImagePath, imageSizes, imageExts } from "./../../utils.js"
|
|
2
6
|
|
|
3
7
|
/** @import * as Votive from "votive" */
|
|
4
8
|
|
|
9
|
+
|
|
5
10
|
/** @type {Votive.ProcessorWrite} */
|
|
6
11
|
async function writeImage(destination, database, config) {
|
|
12
|
+
const { uuid, sourcePath } = destination.abstract
|
|
13
|
+
|
|
14
|
+
const defaultFormat = path.extname(sourcePath)
|
|
15
|
+
|
|
16
|
+
const image = sharp(destination.path)
|
|
17
|
+
|
|
18
|
+
const images = imageSizes.flatMap(size => {
|
|
19
|
+
[...imageExts, defaultFormat].map(ext => {
|
|
20
|
+
const filePath = createImagePath({
|
|
21
|
+
targetFilePath: sourcePath,
|
|
22
|
+
size,
|
|
23
|
+
ext,
|
|
24
|
+
targetDirectory: config.destinationFolder,
|
|
25
|
+
uuid
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return image.clone().resize(size).toFile(filePath)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
await Promise.all(images)
|
|
33
|
+
|
|
7
34
|
const buffer = await fs.readFile(destination.path)
|
|
35
|
+
|
|
8
36
|
return {
|
|
9
37
|
data: buffer,
|
|
10
38
|
}
|
|
@@ -12,11 +40,13 @@ async function writeImage(destination, database, config) {
|
|
|
12
40
|
|
|
13
41
|
/** @type {Votive.ProcessorRead} */
|
|
14
42
|
function readImagePath(string) {
|
|
43
|
+
const uuid =randomUUID()
|
|
15
44
|
|
|
16
45
|
const data = {
|
|
17
46
|
metadata: {},
|
|
18
47
|
abstract: {
|
|
19
|
-
|
|
48
|
+
sourcePath: string,
|
|
49
|
+
uuid
|
|
20
50
|
}
|
|
21
51
|
}
|
|
22
52
|
|
|
@@ -19,7 +19,7 @@ import { h } from 'hastscript'
|
|
|
19
19
|
import { normalizeHeadings } from 'mdast-normalize-headings'
|
|
20
20
|
import { readFileSync } from "fs"
|
|
21
21
|
import { remove } from "unist-util-remove"
|
|
22
|
-
import { testURL, testHashtags, createHashtagPage, toTitleCase, hashtagRegexSingle } from "./../../utils.js"
|
|
22
|
+
import { testURL, testHashtags, createHashtagPage, toTitleCase, hashtagRegexSingle, createImagePaths, imageSizes, imageExts } from "./../../utils.js"
|
|
23
23
|
import { toHast } from 'mdast-util-to-hast'
|
|
24
24
|
import { toString as hastToString } from 'hast-util-to-string'
|
|
25
25
|
import { toString as mdastToString } from 'mdast-util-to-string'
|
|
@@ -27,6 +27,7 @@ import { unified } from "unified"
|
|
|
27
27
|
import { visit } from "unist-util-visit"
|
|
28
28
|
import toc from "@jsdevtools/rehype-toc"
|
|
29
29
|
import slug from "rehype-slug"
|
|
30
|
+
import * as unpic from "unpic"
|
|
30
31
|
|
|
31
32
|
const VOWEL_DIR = path.normalize(path.join(import.meta.dirname, "../../"))
|
|
32
33
|
|
|
@@ -50,7 +51,86 @@ User-agent: CCBot
|
|
|
50
51
|
Disallow: /`
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} imagePath
|
|
57
|
+
* @param {Votive.Database} database
|
|
58
|
+
* @param {string} dependent
|
|
59
|
+
*/
|
|
60
|
+
function createDynamicImage(imagePath, database, dependent, alt, itemprop) {
|
|
61
|
+
// TODO: Hardcode image height and width
|
|
62
|
+
const parsed = path.parse(imagePath);
|
|
63
|
+
const isURL = testURL(imagePath)
|
|
64
|
+
const isImg = !parsed.ext.search(/^.(png|jpeg|jpg)$/);
|
|
65
|
+
|
|
66
|
+
if (isURL) {
|
|
67
|
+
const parsedImageURL = unpic.parseUrl(imagePath)
|
|
68
|
+
if (parsedImageURL) {
|
|
69
|
+
|
|
70
|
+
const sources = [...imageExts, "jpeg"].map((format, index) => {
|
|
71
|
+
|
|
72
|
+
const isImg = index === imageExts.length
|
|
73
|
+
// TODO: Ignore images that have options already specified
|
|
74
|
+
const urls = imageSizes.map(size => {
|
|
75
|
+
return unpic.transformUrl({
|
|
76
|
+
format: format,
|
|
77
|
+
url: parsedImageURL.src,
|
|
78
|
+
provider: parsedImageURL.cdn,
|
|
79
|
+
width: size
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const sizes = urls.map((url, index) => `${url} ${imageSizes[index]}w`).join(", ")
|
|
84
|
+
|
|
85
|
+
return h(isImg ? "img" : "source", {
|
|
86
|
+
type: "image/" + format,
|
|
87
|
+
srcset: sizes,
|
|
88
|
+
src: isImg && urls.at(-1),
|
|
89
|
+
loading: isImg && "lazy",
|
|
90
|
+
sizes: "100vw",
|
|
91
|
+
alt: isImg && alt
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return h("picture", { itemprop: itemprop && "image" }, sources)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!isImg) return
|
|
100
|
+
|
|
101
|
+
const relativePath = imagePath.startsWith("/") ? path.relative("/", imagePath) : imagePath
|
|
102
|
+
const image = database.target.getWithTrackers(relativePath, dependent)
|
|
103
|
+
console.log({ image, relativePath })
|
|
104
|
+
if (!image) return
|
|
105
|
+
const formats = createImagePaths(image.abstract.sourcePath, "./", image.abstract.uuid)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
const sources = formats.map((format, index) => {
|
|
109
|
+
const isImg = index === formats.length - 1
|
|
110
|
+
|
|
111
|
+
const sizes = format.map((size, index) => `/${size} ${imageSizes[index]}w`).join(", ")
|
|
112
|
+
const type = "image/" + path.extname(format[0]).slice(1)
|
|
113
|
+
return h(isImg ? "img" : "source", {
|
|
114
|
+
type,
|
|
115
|
+
srcset: sizes,
|
|
116
|
+
loading: isImg && "lazy",
|
|
117
|
+
src: isImg && "/" + format.at(-1),
|
|
118
|
+
sizes: "100vw",
|
|
119
|
+
alt: isImg && alt
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
return h("picture", { itemprop: itemprop && "image" }, sources)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {object} metadata
|
|
129
|
+
* @param {string} url
|
|
130
|
+
* @param {Votive.Database} database
|
|
131
|
+
* @param {Votive.VotiveConfig} config
|
|
132
|
+
*/
|
|
133
|
+
function makeHead(metadata, url, database, config) {
|
|
54
134
|
|
|
55
135
|
const treeMainHead = []
|
|
56
136
|
|
|
@@ -75,19 +155,16 @@ function makeHead(metadata, url) {
|
|
|
75
155
|
)
|
|
76
156
|
}
|
|
77
157
|
|
|
78
|
-
if (metadata.image) treeMainHead.push(
|
|
79
|
-
h("img", {
|
|
80
|
-
src: metadata.image,
|
|
81
|
-
itemprop: "image"
|
|
82
|
-
})
|
|
83
|
-
)
|
|
84
158
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
if (metadata.image || (metadata.first_image && url)) {
|
|
162
|
+
const metaImage = metadata.image || metadata.first_image
|
|
163
|
+
|
|
164
|
+
treeMainHead.push(
|
|
165
|
+
createDynamicImage(metaImage, database, url, null, true)
|
|
166
|
+
)
|
|
167
|
+
}
|
|
91
168
|
|
|
92
169
|
if (metadata.fm_description) treeMainHead.push(
|
|
93
170
|
h("p",
|
|
@@ -991,7 +1068,12 @@ function writeFile(destination, database, config) {
|
|
|
991
1068
|
|
|
992
1069
|
const treeContentSlugged = slugger.runSync(treeContent)
|
|
993
1070
|
|
|
994
|
-
const treeMainHead = makeHead(metadata)
|
|
1071
|
+
const treeMainHead = makeHead(metadata, null, database, config)
|
|
1072
|
+
|
|
1073
|
+
visit(treeContent, { tagName: "img" }, (node, index, parent) => {
|
|
1074
|
+
const { src, alt } = node.properties
|
|
1075
|
+
parent.children.splice(index, 1, createDynamicImage(src, database, destination.path, alt))
|
|
1076
|
+
})
|
|
995
1077
|
|
|
996
1078
|
visit(treeContent, testPaths, ({ children: [child] }, i, p) => {
|
|
997
1079
|
|
|
@@ -1011,7 +1093,7 @@ function writeFile(destination, database, config) {
|
|
|
1011
1093
|
const target = database.target.getWithTrackers(targetFilePath, destination.path)
|
|
1012
1094
|
|
|
1013
1095
|
if (target) {
|
|
1014
|
-
const article = h('article', makeHead(target.metadata, target.metadata.prettyURL))
|
|
1096
|
+
const article = h('article', makeHead(target.metadata, target.metadata.prettyURL, database, config))
|
|
1015
1097
|
|
|
1016
1098
|
p.children.splice(i, 1, article)
|
|
1017
1099
|
}
|
|
@@ -1046,7 +1128,7 @@ function writeFile(destination, database, config) {
|
|
|
1046
1128
|
const list = h(`ul.${dirClasses}`,
|
|
1047
1129
|
targets.map(target => {
|
|
1048
1130
|
return h('li',
|
|
1049
|
-
h('article', makeHead(target.metadata, target.metadata.prettyURL))
|
|
1131
|
+
h('article', makeHead(target.metadata, target.metadata.prettyURL, database, config))
|
|
1050
1132
|
)
|
|
1051
1133
|
})
|
|
1052
1134
|
)
|
package/utils.js
CHANGED
|
@@ -1,7 +1,47 @@
|
|
|
1
1
|
import {fromMarkdown} from 'mdast-util-from-markdown'
|
|
2
2
|
import {toHast} from 'mdast-util-to-hast'
|
|
3
3
|
import {toHtml} from 'hast-util-to-html'
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { randomUUID } from "node:crypto"
|
|
4
6
|
|
|
7
|
+
export const imageSizes = [414, 768, 1440, 1920]
|
|
8
|
+
export const imageExts = ["avif", "webp"]
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} params
|
|
12
|
+
* @property {string} targetFilePath
|
|
13
|
+
* @property {number} size
|
|
14
|
+
* @property {string} targetDirectory
|
|
15
|
+
* @property {string} uuid
|
|
16
|
+
*/
|
|
17
|
+
export function createImagePath({ targetFilePath, size, ext, targetDirectory, uuid }) {
|
|
18
|
+
const { dir, name } = path.parse(targetFilePath)
|
|
19
|
+
return path.format({
|
|
20
|
+
dir: path.relative(".", path.normalize(path.join(targetDirectory, dir))),
|
|
21
|
+
name: `${name}-${uuid}-${size}`,
|
|
22
|
+
ext
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} targetFilePath
|
|
28
|
+
* @param {string} targetDirectory
|
|
29
|
+
* @param {string} uuid
|
|
30
|
+
*/
|
|
31
|
+
export function createImagePaths(targetFilePath, targetDirectory, uuid) {
|
|
32
|
+
const defaultExt = path.extname(targetFilePath)
|
|
33
|
+
return [...imageExts, defaultExt].map(ext => {
|
|
34
|
+
return imageSizes.map(size => {
|
|
35
|
+
return createImagePath({
|
|
36
|
+
targetFilePath,
|
|
37
|
+
size,
|
|
38
|
+
ext,
|
|
39
|
+
targetDirectory,
|
|
40
|
+
uuid
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
}
|
|
5
45
|
|
|
6
46
|
/** @param {string} text */
|
|
7
47
|
export function testURL(text) {
|