vowel 0.6.2 → 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 +24 -18
- 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 -1063
- 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/DefaultStyles.css +505 -385
- package/stylesheets/SyntaxHighlightingStyles.css +237 -0
- package/stylesheets/TypographyStyles.css +7 -64
- package/stylesheets/brand-colors.css +119 -0
- package/utils.js +2 -2
package/README.md
CHANGED
|
@@ -9,6 +9,11 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
9
9
|
|
|
10
10
|
## Roadmap
|
|
11
11
|
|
|
12
|
+
### Priorities
|
|
13
|
+
|
|
14
|
+
- [ ] Create CONTRIBUTING.md
|
|
15
|
+
- [ ]
|
|
16
|
+
|
|
12
17
|
### Launch checklist
|
|
13
18
|
|
|
14
19
|
- [x] CLI
|
|
@@ -17,14 +22,15 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
17
22
|
- [x] Route patterns
|
|
18
23
|
- [x] Post list metadata
|
|
19
24
|
- [.] Transaction management
|
|
25
|
+
- [ ] Default 404 pages
|
|
20
26
|
|
|
21
27
|
- [.] Fixes
|
|
22
28
|
- [x] Global nav sublinks
|
|
23
29
|
- [x] Hot reload
|
|
24
|
-
- [
|
|
30
|
+
- [x] Explicit asset handling
|
|
25
31
|
- [.] Change "job" to "resources"
|
|
26
32
|
- [.] ReadResource types
|
|
27
|
-
- [
|
|
33
|
+
- [x] Processor utilities param
|
|
28
34
|
- [.] Delete old SQLite logic
|
|
29
35
|
- [.] Make metadata explicit in SQLiteTarget type
|
|
30
36
|
- [x] Add SQLite begin/commit logic
|
|
@@ -35,26 +41,27 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
35
41
|
- [x] Homepage in global nav
|
|
36
42
|
- [x] Metadata in header
|
|
37
43
|
- [x] Page list sort
|
|
38
|
-
- [
|
|
44
|
+
- [x] Limit in sort
|
|
39
45
|
- [x] Metafiles
|
|
40
46
|
- [x] Robots.txt
|
|
41
47
|
- [x] Sitemap.xml
|
|
42
48
|
- [x] RSS
|
|
43
|
-
- [
|
|
49
|
+
- [x] 404.html (just make 404.md)
|
|
44
50
|
- [ ] Markup
|
|
45
|
-
- [
|
|
51
|
+
- [x] Image as figure
|
|
46
52
|
- [x] Admonitions
|
|
47
53
|
- [x] Heading anchors
|
|
48
54
|
- [ ] GFM emoji
|
|
49
55
|
- [ ] Mermaid
|
|
50
56
|
- [x] Slogan
|
|
51
57
|
- [x] TOC
|
|
58
|
+
- [x] ==mark==
|
|
52
59
|
- [ ] Non-urgent fixes / improvements
|
|
53
60
|
- [ ] Compare metadata on file update
|
|
54
61
|
- [x] Prune/cleanup step
|
|
55
|
-
- [
|
|
62
|
+
- [x] Dependency type (file, settings, folder)
|
|
56
63
|
- [ ] New systems
|
|
57
|
-
- [
|
|
64
|
+
- [x] Tags
|
|
58
65
|
- [ ] External links
|
|
59
66
|
- [ ] Syntax highlighting
|
|
60
67
|
- [ ] Social links
|
|
@@ -68,7 +75,7 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
68
75
|
- [ ] GetMany "extension" filter
|
|
69
76
|
- [ ] Delete settings logic
|
|
70
77
|
- [ ] Update settings logic
|
|
71
|
-
- [
|
|
78
|
+
- [x] Logical operators for filters
|
|
72
79
|
- [ ] Customize menus
|
|
73
80
|
- [ ] More file handling plugins
|
|
74
81
|
- [ ] Fonts
|
|
@@ -91,21 +98,20 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
91
98
|
- [ ] Styles
|
|
92
99
|
- [x] .css
|
|
93
100
|
- [x] Bundling
|
|
94
|
-
- [
|
|
95
|
-
- [
|
|
96
|
-
- [
|
|
101
|
+
- [x] PDFs
|
|
102
|
+
- [x] Videos
|
|
103
|
+
- [x] Arbitrary data
|
|
97
104
|
- [x] Add homepage to global nav
|
|
98
105
|
- [x] Page lists
|
|
99
106
|
- [x] Dates
|
|
100
107
|
- [x] robots.txt
|
|
101
108
|
- [x] sitemap.xml
|
|
102
|
-
- [
|
|
109
|
+
- [x] 404.html
|
|
103
110
|
- [ ] Better URL normalization (see notes)
|
|
104
111
|
- [x] Name casing
|
|
105
112
|
- [x] TUI
|
|
106
113
|
- [x] Create settings.md
|
|
107
114
|
- [x] Site title
|
|
108
|
-
- [x] Domain
|
|
109
115
|
- [ ] Webmentions
|
|
110
116
|
- [x] Logo
|
|
111
117
|
- [x] Wordmark
|
|
@@ -121,7 +127,7 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
121
127
|
- [x] Custom CSS
|
|
122
128
|
- [x] styles.css
|
|
123
129
|
- [ ] Any CSS file
|
|
124
|
-
- [
|
|
130
|
+
- [x] Tags
|
|
125
131
|
- [ ] Date format settings
|
|
126
132
|
- [ ] ::mark::
|
|
127
133
|
- [x] Infer images
|
|
@@ -136,7 +142,7 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
136
142
|
- [ ] Handle external links
|
|
137
143
|
- [x] Admonitions
|
|
138
144
|
- [ ] Use hgroup for site title, page title, etc
|
|
139
|
-
- [
|
|
145
|
+
- [x] Images as `<figure>`
|
|
140
146
|
- [ ] Hidden routes
|
|
141
147
|
- [ ] Frontmatter settings
|
|
142
148
|
- [ ] HTML
|
|
@@ -156,8 +162,8 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
156
162
|
- [ ] Image optimization (unpic)
|
|
157
163
|
- [ ] [SVG by mask](https://pqina.nl/blog/set-svg-background-image-fill-color/) and [CSS icons](https://antfu.me/posts/icons-in-pure-css)
|
|
158
164
|
- [ ] WYSIWYG editor
|
|
159
|
-
- [
|
|
160
|
-
- [
|
|
165
|
+
- [x] Better signals
|
|
166
|
+
- [x] File-written callback
|
|
161
167
|
- [ ] Themes
|
|
162
168
|
- [ ] Deploy
|
|
163
169
|
- [ ] Cloudflare pages
|
|
@@ -173,7 +179,7 @@ See the docs: [vowel.cc](https://vowel.cc).
|
|
|
173
179
|
- [ ] Image
|
|
174
180
|
- [ ] URL
|
|
175
181
|
- [ ] Date
|
|
176
|
-
- [
|
|
182
|
+
- [x] TOC
|
|
177
183
|
- [ ] Versioning/publishing script
|
|
178
184
|
- [ ] Verify all element types from Obsidian
|
|
179
185
|
- [ ] Order/position/kanban
|
package/bin.js
CHANGED
|
@@ -18,7 +18,7 @@ import init from "./index.js"
|
|
|
18
18
|
/** @param {boolean} verbose */
|
|
19
19
|
async function removeCache(verbose) {
|
|
20
20
|
try {
|
|
21
|
-
await fs.rm(
|
|
21
|
+
await fs.rm(config.databasePath)
|
|
22
22
|
if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "cache cleared")}`)
|
|
23
23
|
} catch (e) {
|
|
24
24
|
if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "no database cache found")}`)
|
|
@@ -28,7 +28,7 @@ async function removeCache(verbose) {
|
|
|
28
28
|
/** @param {boolean} verbose */
|
|
29
29
|
async function removeDB(verbose) {
|
|
30
30
|
try {
|
|
31
|
-
await fs.rm(
|
|
31
|
+
await fs.rm(config.targetFolder, { recursive: true, force: true })
|
|
32
32
|
if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "output cleared")}`)
|
|
33
33
|
} catch (e) {
|
|
34
34
|
if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "no output cache found")}`)
|
|
@@ -190,14 +190,51 @@ The few configurations that Vowel uses live in a 'settings.md' file at the root
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Runs one registered command directly and exits - no dev server, no
|
|
195
|
+
* wizard, no cache-wiping. Reuses the exact handler a live "Publish"
|
|
196
|
+
* button would trigger over WS (see voot's runCommand/handleCommand) -
|
|
197
|
+
* this is just the other invocation path for the same registered
|
|
198
|
+
* function, meant for CI/automated use (e.g. `vowel --command deploy`).
|
|
199
|
+
* @param {string} name
|
|
200
|
+
* @param {string} [payloadJSON]
|
|
201
|
+
*/
|
|
202
|
+
async function runCLICommand(name, payloadJSON) {
|
|
203
|
+
const { runCommand } = await import("voot")
|
|
204
|
+
|
|
205
|
+
let payload
|
|
206
|
+
try {
|
|
207
|
+
payload = payloadJSON ? JSON.parse(payloadJSON) : undefined
|
|
208
|
+
} catch (e) {
|
|
209
|
+
console.error(`--payload must be valid JSON: ${e.message}`)
|
|
210
|
+
process.exitCode = 1
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const data = await runCommand(config, name, payload, message => {
|
|
216
|
+
console.info(message?.message ?? message)
|
|
217
|
+
})
|
|
218
|
+
if (data !== undefined) console.info(JSON.stringify(data))
|
|
219
|
+
} catch (e) {
|
|
220
|
+
console.error(e?.message || e)
|
|
221
|
+
process.exitCode = 1
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
193
225
|
async function main() {
|
|
194
226
|
const args = parse(process.argv.slice(2))
|
|
195
227
|
|
|
196
|
-
|
|
228
|
+
if (args.command) {
|
|
229
|
+
await runCLICommand(args.command, args.payload)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const dbExists = await exists(config.databasePath)
|
|
197
234
|
|
|
198
235
|
await removeCache(args.logging === "verbose")
|
|
199
236
|
await removeDB(args.logging === "verbose")
|
|
200
|
-
await fs.mkdir(config.
|
|
237
|
+
await fs.mkdir(config.targetFolder, { recursive: true })
|
|
201
238
|
|
|
202
239
|
if (!args.skip && !dbExists) {
|
|
203
240
|
const loading = votive({ ...config, verbose: false })
|
package/config.js
CHANGED
|
@@ -1,22 +1,41 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import { systemDirectoryFor } from "votive/internals"
|
|
1
3
|
import vowelImagesPlugin from "./plugins/images/index.js"
|
|
2
4
|
import vowelStylesPlugin from "./plugins/styles/index.js"
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
5
|
+
import vowelReadMarkdownPlugin from "./plugins/markdown/index.js"
|
|
6
|
+
import vowelWriteRobotsPlugin from "./plugins/robots/index.js"
|
|
7
|
+
import vowelWriteXMLPlugin from "./plugins/xml/index.js"
|
|
6
8
|
import vowelVectorPlugin from "./plugins/vectors/index.js"
|
|
7
9
|
import vowelFontsPlugin from "./plugins/fonts/index.js"
|
|
10
|
+
import vowelWriteHTMLPlugin from "./plugins/html/index.js"
|
|
11
|
+
import vowelURLsPlugin from "./plugins/urls/index.js"
|
|
8
12
|
|
|
9
13
|
/** @import {VotiveConfig} from "votive" */
|
|
14
|
+
|
|
15
|
+
const sourceFolder = "."
|
|
16
|
+
|
|
17
|
+
// Vowel targets non-developer end users (via whatever app embeds it),
|
|
18
|
+
// who could be confused by an `output`/.votive.db/.cache showing up in
|
|
19
|
+
// their project folder - keep all three in an OS-appropriate system
|
|
20
|
+
// directory instead, keyed per-project so multiple Vowel sites never
|
|
21
|
+
// collide. Override any of the three below if you want them back in
|
|
22
|
+
// the project directory (or somewhere else entirely).
|
|
23
|
+
const systemDirectory = systemDirectoryFor(sourceFolder)
|
|
24
|
+
|
|
10
25
|
export const config = {
|
|
11
|
-
sourceFolder
|
|
12
|
-
|
|
26
|
+
sourceFolder,
|
|
27
|
+
targetFolder: path.join(systemDirectory, "output"),
|
|
28
|
+
databasePath: path.join(systemDirectory, ".votive.db"),
|
|
29
|
+
cacheDirectory: path.join(systemDirectory, ".cache"),
|
|
13
30
|
plugins: [
|
|
14
|
-
|
|
31
|
+
vowelReadMarkdownPlugin,
|
|
15
32
|
vowelImagesPlugin,
|
|
16
33
|
vowelStylesPlugin,
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
vowelWriteRobotsPlugin,
|
|
35
|
+
vowelWriteXMLPlugin,
|
|
19
36
|
vowelVectorPlugin,
|
|
20
|
-
vowelFontsPlugin
|
|
37
|
+
vowelFontsPlugin,
|
|
38
|
+
vowelURLsPlugin,
|
|
39
|
+
vowelWriteHTMLPlugin
|
|
21
40
|
]
|
|
22
41
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vowel",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.4",
|
|
5
5
|
"bin": "bin.js",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"scripts": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"hast-util-from-html": "^2.0.3",
|
|
29
29
|
"hast-util-to-string": "^3.0.1",
|
|
30
30
|
"hastscript": "^9.0.1",
|
|
31
|
+
"highlight.js": "^11.12.0",
|
|
31
32
|
"js-yaml": "^4.1.0",
|
|
32
33
|
"lightningcss": "^1.32.0",
|
|
33
34
|
"mdast": "^2.3.2",
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"mdast-util-gfm-strikethrough": "^2.0.0",
|
|
38
39
|
"mdast-util-gfm-table": "^2.0.0",
|
|
39
40
|
"mdast-util-gfm-task-list-item": "^2.0.0",
|
|
41
|
+
"mdast-util-highlight-mark": "^1.2.2",
|
|
40
42
|
"mdast-util-to-hast": "^13.2.1",
|
|
41
43
|
"mdast-util-to-markdown": "^2.1.2",
|
|
42
44
|
"mdast-util-to-string": "^4.0.0",
|
|
@@ -44,10 +46,12 @@
|
|
|
44
46
|
"micromark-extension-gfm-footnote": "^2.1.0",
|
|
45
47
|
"micromark-extension-gfm-table": "^2.1.1",
|
|
46
48
|
"micromark-extension-gfm-task-list-item": "^2.1.0",
|
|
49
|
+
"micromark-extension-highlight-mark": "^1.2.0",
|
|
47
50
|
"postcss": "^8.5.9",
|
|
48
51
|
"rehype": "^13.0.2",
|
|
49
52
|
"rehype-document": "^7.0.3",
|
|
50
53
|
"rehype-github-alert": "^1.0.0",
|
|
54
|
+
"rehype-highlight": "^7.0.2",
|
|
51
55
|
"rehype-parse": "^9.0.1",
|
|
52
56
|
"rehype-preset-minify": "^7.0.1",
|
|
53
57
|
"rehype-slug": "^6.0.0",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import votive from "votive"
|
|
2
|
+
import { spawn } from "node:child_process"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Publishes a full build to Cloudflare Pages via `wrangler pages deploy`.
|
|
6
|
+
*
|
|
7
|
+
* Always runs a complete build first, regardless of whether a dev server
|
|
8
|
+
* happens to already be running against this config - a command's
|
|
9
|
+
* context (config, notify) has no reference to a live server's own build
|
|
10
|
+
* queue, and this needs to behave identically whether triggered from the
|
|
11
|
+
* CLI (no server at all, see voot's runCommand) or a live "Publish"
|
|
12
|
+
* button over WS. Modest redundant cost when a dev server IS already
|
|
13
|
+
* running (bundle() is cheap when nothing's stale), traded for one
|
|
14
|
+
* deploy command that works the same everywhere.
|
|
15
|
+
*
|
|
16
|
+
* Shells out to `wrangler` rather than calling Cloudflare's REST API
|
|
17
|
+
* directly - Pages deploys require content-hashing every asset and
|
|
18
|
+
* building a manifest in Cloudflare's specific format, which wrangler
|
|
19
|
+
* already implements and maintains. `wrangler` reads
|
|
20
|
+
* CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID from the environment
|
|
21
|
+
* natively; this never touches credentials itself, and none should ever
|
|
22
|
+
* be added to `config` (it isn't a place secrets should live).
|
|
23
|
+
*
|
|
24
|
+
* @param {any} payload
|
|
25
|
+
* @param {{ config: import("votive").VotiveConfig & { cloudflareProjectName?: string }, notify: (message: object) => void }} context
|
|
26
|
+
*/
|
|
27
|
+
async function deployToCloudflarePages(payload, { config, notify }) {
|
|
28
|
+
if (!config.cloudflareProjectName) throw new Error("config.cloudflareProjectName is required to deploy")
|
|
29
|
+
|
|
30
|
+
notify({ status: "progress", message: "Building..." })
|
|
31
|
+
|
|
32
|
+
const queue = await votive({ ...config, verbose: false })
|
|
33
|
+
const { runBuffers, runFetches } = await queue()
|
|
34
|
+
|
|
35
|
+
// Unlike a dev server's own rebuilds, a deploy needs everything done -
|
|
36
|
+
// there's no "next edit" to stay responsive for, so these are awaited
|
|
37
|
+
// fully instead of fired via runDeferred().
|
|
38
|
+
if (runBuffers) await runBuffers()
|
|
39
|
+
if (runFetches) await runFetches()
|
|
40
|
+
|
|
41
|
+
notify({ status: "progress", message: `Publishing ${config.targetFolder} to Cloudflare Pages...` })
|
|
42
|
+
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const proc = spawn(
|
|
45
|
+
"wrangler",
|
|
46
|
+
["pages", "deploy", config.targetFolder, "--project-name", config.cloudflareProjectName],
|
|
47
|
+
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
// Piped, not "inherit" - this can run with no terminal attached at
|
|
51
|
+
// all (triggered over WS from a browser), so wrangler's own output
|
|
52
|
+
// has to go through notify() to reach whoever's actually watching.
|
|
53
|
+
proc.stdout.on("data", chunk => notify({ status: "progress", message: chunk.toString() }))
|
|
54
|
+
proc.stderr.on("data", chunk => notify({ status: "progress", message: chunk.toString() }))
|
|
55
|
+
|
|
56
|
+
proc.on("error", reject) // e.g. wrangler isn't installed
|
|
57
|
+
proc.on("exit", code => {
|
|
58
|
+
if (code === 0) resolve({ deployed: true })
|
|
59
|
+
else reject(new Error(`wrangler exited with code ${code}`))
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Not part of vowel's default plugin list (see config.js) - deploying to
|
|
66
|
+
* Cloudflare Pages specifically is an opt-in choice a site adds for
|
|
67
|
+
* itself, along with its own `cloudflareProjectName`, not something
|
|
68
|
+
* every vowel site should get by default.
|
|
69
|
+
* @type {import("votive").VotivePlugin}
|
|
70
|
+
*/
|
|
71
|
+
const vowelDeployCloudflarePlugin = {
|
|
72
|
+
name: "vowel-deploy-cloudflare",
|
|
73
|
+
commands: {
|
|
74
|
+
deploy: deployToCloudflarePages
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default vowelDeployCloudflarePlugin
|
package/plugins/fonts/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { readFile } from "node:fs/promises"
|
|
|
2
2
|
/** @import * as Votive from "votive" */
|
|
3
3
|
|
|
4
4
|
/** @type {Votive.ProcessorWrite} */
|
|
5
|
-
async function writeFile(
|
|
6
|
-
const buffer = await readFile(
|
|
5
|
+
async function writeFile(target, settings, api, config) {
|
|
6
|
+
const buffer = await readFile(target.abstract.filePath)
|
|
7
7
|
return {
|
|
8
8
|
data: buffer,
|
|
9
9
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { testURL, createImagePaths, imageSizes, imageExts } from "./../../utils.js"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { h } from 'hastscript'
|
|
4
|
+
import * as unpic from "unpic"
|
|
5
|
+
|
|
6
|
+
/** @import * as Votive from "votive" */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} imagePath
|
|
10
|
+
* @param {import("votive").PluginAPI} api
|
|
11
|
+
* @param {string} alt
|
|
12
|
+
* @param {boolean} [itemprop]
|
|
13
|
+
*/
|
|
14
|
+
function createDynamicImage(imagePath, api, alt, itemprop) {
|
|
15
|
+
// TODO: Hardcode image height and width
|
|
16
|
+
const parsed = path.parse(imagePath);
|
|
17
|
+
const isURL = testURL(imagePath)
|
|
18
|
+
const isImg = !parsed.ext.search(/^.(png|jpeg|jpg)$/);
|
|
19
|
+
|
|
20
|
+
if (isURL) {
|
|
21
|
+
const parsedImageURL = unpic.parseUrl(imagePath)
|
|
22
|
+
if (parsedImageURL) {
|
|
23
|
+
|
|
24
|
+
const sources = [...imageExts, "jpeg"].map((format, index) => {
|
|
25
|
+
|
|
26
|
+
const isImg = index === imageExts.length
|
|
27
|
+
// TODO: Ignore images that have options already specified
|
|
28
|
+
const urls = imageSizes.map(size => {
|
|
29
|
+
return unpic.transformUrl({
|
|
30
|
+
format: format,
|
|
31
|
+
url: parsedImageURL.src,
|
|
32
|
+
provider: parsedImageURL.cdn,
|
|
33
|
+
width: size
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const sizes = urls.map((url, index) => `${url} ${imageSizes[index]}w`).join(", ")
|
|
38
|
+
|
|
39
|
+
return h(isImg ? "img" : "source", {
|
|
40
|
+
type: "image/" + format,
|
|
41
|
+
srcset: sizes,
|
|
42
|
+
src: isImg && urls.at(-1),
|
|
43
|
+
loading: isImg && "lazy",
|
|
44
|
+
sizes: "100vw",
|
|
45
|
+
alt: isImg && alt
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
return h("picture", { itemprop: itemprop && "image" }, sources)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isImg) return
|
|
54
|
+
|
|
55
|
+
const relativePath = imagePath.startsWith("/") ? path.relative("/", imagePath) : imagePath
|
|
56
|
+
const image = api.target(relativePath)
|
|
57
|
+
if (!image) return
|
|
58
|
+
const formats = createImagePaths(image.abstract.sourcePath, "./", image.abstract.uuid)
|
|
59
|
+
|
|
60
|
+
const sources = formats.map((format, index) => {
|
|
61
|
+
const isImg = index === formats.length - 1
|
|
62
|
+
|
|
63
|
+
const sizes = format.map((size, index) => `/${size} ${imageSizes[index]}w`).join(", ")
|
|
64
|
+
const type = "image/" + path.extname(format[0]).slice(1)
|
|
65
|
+
return h(isImg ? "img" : "source", {
|
|
66
|
+
type,
|
|
67
|
+
srcset: sizes,
|
|
68
|
+
loading: isImg && "lazy",
|
|
69
|
+
src: isImg && "/" + format.at(-1),
|
|
70
|
+
sizes: "100vw",
|
|
71
|
+
alt: isImg && alt
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return h("picture", { itemprop: itemprop && "image" }, sources)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default createDynamicImage
|