nuxt-content-assets 0.10.3 → 1.1.0
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 +53 -17
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +13 -8
- package/dist/runtime/options.mjs +1 -1
- package/dist/runtime/plugin.mjs +17 -5
- package/dist/runtime/services/assets.d.ts +1 -1
- package/dist/runtime/services/assets.mjs +5 -5
- package/dist/runtime/sockets/factory.d.ts +1 -0
- package/dist/runtime/sockets/factory.mjs +11 -4
- package/dist/runtime/utils/build.d.ts +8 -0
- package/dist/runtime/utils/build.mjs +12 -0
- package/dist/runtime/utils/index.d.ts +2 -1
- package/dist/runtime/utils/index.mjs +2 -1
- package/dist/runtime/utils/{assert.d.ts → path.d.ts} +8 -0
- package/dist/runtime/utils/{assert.mjs → path.mjs} +8 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -45,6 +45,24 @@ Almost as much as being in the sea!
|
|
|
45
45
|
|
|
46
46
|
At build time the module [collates and serves](#how-it-works) assets and content together.
|
|
47
47
|
|
|
48
|
+
### Features
|
|
49
|
+
|
|
50
|
+
Built on top of [Nuxt Content](https://github.com/nuxt/content/) and compatible with any Nuxt Content project or theme, including [Docus](https://github.com/nuxt-themes/docus).
|
|
51
|
+
|
|
52
|
+
User experience:
|
|
53
|
+
|
|
54
|
+
- co-locate assets with content files
|
|
55
|
+
- reference assets using relative paths
|
|
56
|
+
- supports any format (image, video, doc)
|
|
57
|
+
|
|
58
|
+
Developer experience:
|
|
59
|
+
|
|
60
|
+
- works with tags and custom components
|
|
61
|
+
- works in markdown and frontmatter
|
|
62
|
+
- file watching and asset live-reload
|
|
63
|
+
- image size injection
|
|
64
|
+
- zero config
|
|
65
|
+
|
|
48
66
|
## Demo
|
|
49
67
|
|
|
50
68
|
To view the demo locally, run:
|
|
@@ -88,7 +106,7 @@ Run the dev server or build and local assets should now be served alongside mark
|
|
|
88
106
|
|
|
89
107
|
Use relative paths anywhere within your documents:
|
|
90
108
|
|
|
91
|
-
```
|
|
109
|
+
```md
|
|
92
110
|
Images
|
|
93
111
|

|
|
94
112
|
|
|
@@ -104,7 +122,7 @@ HTML
|
|
|
104
122
|
|
|
105
123
|
Relative paths can be defined in frontmatter – as long as they are the only value:
|
|
106
124
|
|
|
107
|
-
```
|
|
125
|
+
```md
|
|
108
126
|
---
|
|
109
127
|
title: Portfolio
|
|
110
128
|
images:
|
|
@@ -132,17 +150,23 @@ If you edit an image, video, embed or iframe source, the content will update imm
|
|
|
132
150
|
|
|
133
151
|
### Image sizing
|
|
134
152
|
|
|
135
|
-
|
|
153
|
+
#### HTML
|
|
154
|
+
|
|
155
|
+
The module is [preconfigured](#image-size) to pass image size hints (by default `style`) to generated `<img>` tags:
|
|
136
156
|
|
|
137
157
|
```html
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
>
|
|
158
|
+
<!-- imageSize: 'style' -->
|
|
159
|
+
<img src="/image.jpg" style="aspect-ratio:640/480">
|
|
160
|
+
|
|
161
|
+
<!-- imageSize: 'attrs' -->
|
|
162
|
+
<img src="/image.jpg" width="640" height="480">
|
|
143
163
|
```
|
|
144
164
|
|
|
145
|
-
|
|
165
|
+
Passing image sizes prevents content jumps on page load.
|
|
166
|
+
|
|
167
|
+
#### Prose components
|
|
168
|
+
|
|
169
|
+
If you use [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can use `imageSize: 'attrs'` to [hook into these values](demo/components/temp/ProseImg.vue) via Vue's `$attrs` property:
|
|
146
170
|
|
|
147
171
|
```vue
|
|
148
172
|
<template>
|
|
@@ -158,12 +182,22 @@ export default {
|
|
|
158
182
|
</script>
|
|
159
183
|
```
|
|
160
184
|
|
|
161
|
-
|
|
185
|
+
#### Frontmatter
|
|
186
|
+
|
|
187
|
+
If you pass [frontmatter](demo/content/advanced/gallery.md) to [custom components](demo/components/content/ContentImage.vue) configure `imageSize` as `'src'` to encode the size in `src`:
|
|
162
188
|
|
|
163
189
|
```
|
|
164
|
-
:image-
|
|
190
|
+
:image-content{:src="image"}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The component will receive the updated path which you can parse and implement as you see fit.
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<img class="image-content" src="/image.jpg?width=640&height=480">
|
|
165
197
|
```
|
|
166
198
|
|
|
199
|
+
See demo component [here](demo/components/content/ContentImage.vue).
|
|
200
|
+
|
|
167
201
|
## Configuration
|
|
168
202
|
|
|
169
203
|
The module can be configured in your Nuxt configuration file:
|
|
@@ -176,10 +210,10 @@ export default defineNuxtConfig({
|
|
|
176
210
|
imageSize: 'style',
|
|
177
211
|
|
|
178
212
|
// treat these extensions as content
|
|
179
|
-
contentExtensions: '
|
|
213
|
+
contentExtensions: 'md csv ya?ml json',
|
|
180
214
|
|
|
181
215
|
// output debug messages
|
|
182
|
-
debug:
|
|
216
|
+
debug: false,
|
|
183
217
|
}
|
|
184
218
|
})
|
|
185
219
|
```
|
|
@@ -190,7 +224,7 @@ You can add one or more image size hints to the generated images:
|
|
|
190
224
|
|
|
191
225
|
```ts
|
|
192
226
|
{
|
|
193
|
-
imageSize: 'attrs
|
|
227
|
+
imageSize: 'style attrs src'
|
|
194
228
|
}
|
|
195
229
|
```
|
|
196
230
|
|
|
@@ -200,9 +234,9 @@ Pick from the following switches:
|
|
|
200
234
|
| ------- | ------------------------------------------------------------ |
|
|
201
235
|
| `style` | Adds `style="aspect-ratio:..."` to any `<img>` tag |
|
|
202
236
|
| `attrs` | Adds `width` and `height` attributes to any `<img>` tag |
|
|
203
|
-
| `
|
|
237
|
+
| `src` | Adds `?width=...&height=...` to `src` attribute (frontmatter only) |
|
|
204
238
|
|
|
205
|
-
Note: if you add `attrs`
|
|
239
|
+
Note: if you add *only* `attrs` include the following CSS in your app:
|
|
206
240
|
|
|
207
241
|
```css
|
|
208
242
|
img {
|
|
@@ -211,12 +245,14 @@ img {
|
|
|
211
245
|
}
|
|
212
246
|
```
|
|
213
247
|
|
|
248
|
+
To disable, pass `false`.
|
|
249
|
+
|
|
214
250
|
### Content extensions
|
|
215
251
|
|
|
216
252
|
This setting tells Nuxt Content to ignore anything that is **not** one of these file extensions:
|
|
217
253
|
|
|
218
254
|
```
|
|
219
|
-
|
|
255
|
+
md csv ya?ml json
|
|
220
256
|
```
|
|
221
257
|
|
|
222
258
|
This way, you can use any **other** file type as an asset, without needing to explicitly configure extensions.
|
package/dist/module.d.ts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -35,7 +35,7 @@ function deKey(path) {
|
|
|
35
35
|
|
|
36
36
|
const defaults = {
|
|
37
37
|
// inject image size into the rendered html
|
|
38
|
-
imageSize: "
|
|
38
|
+
imageSize: "style",
|
|
39
39
|
// treat these extensions as content
|
|
40
40
|
contentExtensions: "md csv ya?ml json",
|
|
41
41
|
// output debug messages
|
|
@@ -51,6 +51,9 @@ function getIgnores(extensions2) {
|
|
|
51
51
|
return `^((?!(${matchTokens(extensions2).join("|")})).)*$`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function removeQuery(path) {
|
|
55
|
+
return path.replace(/\?.*$/, "");
|
|
56
|
+
}
|
|
54
57
|
function isExcluded(path) {
|
|
55
58
|
return path.split("/").some((segment) => segment.startsWith(".") || segment.startsWith("_"));
|
|
56
59
|
}
|
|
@@ -59,7 +62,7 @@ function isImage(path) {
|
|
|
59
62
|
return extensions.image.includes(ext);
|
|
60
63
|
}
|
|
61
64
|
function isArticle(path) {
|
|
62
|
-
return path.endsWith(".md");
|
|
65
|
+
return removeQuery(path).endsWith(".md");
|
|
63
66
|
}
|
|
64
67
|
function isAsset(path) {
|
|
65
68
|
return !isArticle(path);
|
|
@@ -124,15 +127,15 @@ function getAssetSizes(srcAbs, hints) {
|
|
|
124
127
|
if (hints.length && isImage(srcAbs)) {
|
|
125
128
|
try {
|
|
126
129
|
const size = getImageSize(srcAbs);
|
|
127
|
-
if (hints.includes("style")) {
|
|
128
|
-
ratio = `${size.width}/${size.height}`;
|
|
129
|
-
}
|
|
130
130
|
if (hints.includes("attrs")) {
|
|
131
131
|
width = size.width;
|
|
132
132
|
height = size.height;
|
|
133
133
|
}
|
|
134
|
-
if (hints.includes("
|
|
135
|
-
|
|
134
|
+
if (hints.includes("style")) {
|
|
135
|
+
ratio = `${size.width}/${size.height}`;
|
|
136
|
+
}
|
|
137
|
+
if (hints.includes("src") || hints.includes("url")) {
|
|
138
|
+
query = `width=${size.width}&height=${size.height}`;
|
|
136
139
|
}
|
|
137
140
|
} catch (err) {
|
|
138
141
|
warn(`could not read image "${srcAbs}`);
|
|
@@ -319,8 +322,10 @@ async function setupSocketServer(channel, handler) {
|
|
|
319
322
|
});
|
|
320
323
|
nuxt._socketServer = server;
|
|
321
324
|
server.on("upgrade", ws.serve);
|
|
325
|
+
const wsUrl = url.replace("http", "ws");
|
|
326
|
+
log(`Websocket listening on "${wsUrl}"`);
|
|
322
327
|
nitro.options.runtimeConfig.public.sockets = {
|
|
323
|
-
wsUrl
|
|
328
|
+
wsUrl
|
|
324
329
|
};
|
|
325
330
|
nitro.hooks.hook("close", async () => {
|
|
326
331
|
await ws.close();
|
package/dist/runtime/options.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { matchTokens } from "./utils/string.mjs";
|
|
2
2
|
export const defaults = {
|
|
3
3
|
// inject image size into the rendered html
|
|
4
|
-
imageSize: "
|
|
4
|
+
imageSize: "style",
|
|
5
5
|
// treat these extensions as content
|
|
6
6
|
contentExtensions: "md csv ya?ml json",
|
|
7
7
|
// output debug messages
|
package/dist/runtime/plugin.mjs
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import Path from "path";
|
|
2
2
|
import { visit, SKIP, CONTINUE } from "unist-util-visit";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildStyle,
|
|
5
|
+
deKey,
|
|
6
|
+
isValidAsset,
|
|
7
|
+
list,
|
|
8
|
+
matchTokens,
|
|
9
|
+
toPath,
|
|
10
|
+
walk,
|
|
11
|
+
removeQuery,
|
|
12
|
+
buildQuery,
|
|
13
|
+
parseQuery
|
|
14
|
+
} from "./utils/index.mjs";
|
|
4
15
|
import { debug, cachePath } from "#nuxt-content-assets";
|
|
5
16
|
import { makeStorage } from "./services/index.mjs";
|
|
6
17
|
async function updateAssets() {
|
|
@@ -51,10 +62,11 @@ const plugin = async (nitro) => {
|
|
|
51
62
|
const filter = (value, key) => !(String(key).startsWith("_") || key === "body");
|
|
52
63
|
walk(file, (value, parent, key) => {
|
|
53
64
|
if (isValidAsset(value)) {
|
|
54
|
-
const { srcAttr, query } = getAsset(value);
|
|
65
|
+
const { srcAttr, query } = getAsset(removeQuery(value));
|
|
55
66
|
if (srcAttr) {
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
const srcUrl = query ? buildQuery(srcAttr, parseQuery(value), query) : srcAttr;
|
|
68
|
+
parent[key] = srcUrl;
|
|
69
|
+
updated.push(`meta: ${key} to "${srcUrl}"`);
|
|
58
70
|
}
|
|
59
71
|
}
|
|
60
72
|
}, filter);
|
|
@@ -82,7 +94,7 @@ const plugin = async (nitro) => {
|
|
|
82
94
|
}
|
|
83
95
|
if (ratio) {
|
|
84
96
|
if (typeof node.props.style === "string") {
|
|
85
|
-
node.props.style
|
|
97
|
+
node.props.style = buildStyle(node.props.style, `aspect-ratio: ${ratio}`);
|
|
86
98
|
} else {
|
|
87
99
|
node.props.style ||= {};
|
|
88
100
|
node.props.style.aspectRatio = ratio;
|
|
@@ -22,7 +22,7 @@ export declare function getAssetPaths(srcDir: string, srcAbs: string): {
|
|
|
22
22
|
* Get asset image sizes
|
|
23
23
|
*
|
|
24
24
|
* @param srcAbs The absolute path to the asset itself
|
|
25
|
-
* @param hints A list of named image size hints,
|
|
25
|
+
* @param hints A list of named image size hints, one of 'attrs', 'style', or 'src'
|
|
26
26
|
*/
|
|
27
27
|
export declare function getAssetSizes(srcAbs: string, hints: string[]): {
|
|
28
28
|
width: number | undefined;
|
|
@@ -19,15 +19,15 @@ export function getAssetSizes(srcAbs, hints) {
|
|
|
19
19
|
if (hints.length && isImage(srcAbs)) {
|
|
20
20
|
try {
|
|
21
21
|
const size = getImageSize(srcAbs);
|
|
22
|
-
if (hints.includes("style")) {
|
|
23
|
-
ratio = `${size.width}/${size.height}`;
|
|
24
|
-
}
|
|
25
22
|
if (hints.includes("attrs")) {
|
|
26
23
|
width = size.width;
|
|
27
24
|
height = size.height;
|
|
28
25
|
}
|
|
29
|
-
if (hints.includes("
|
|
30
|
-
|
|
26
|
+
if (hints.includes("style")) {
|
|
27
|
+
ratio = `${size.width}/${size.height}`;
|
|
28
|
+
}
|
|
29
|
+
if (hints.includes("src") || hints.includes("url")) {
|
|
30
|
+
query = `width=${size.width}&height=${size.height}`;
|
|
31
31
|
}
|
|
32
32
|
} catch (err) {
|
|
33
33
|
warn(`could not read image "${srcAbs}`);
|
|
@@ -46,10 +46,17 @@ export function createWebSocket() {
|
|
|
46
46
|
ws.send(JSON.stringify(data));
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
+
let retries = 0;
|
|
50
|
+
const url = useRuntimeConfig().public.sockets?.wsUrl;
|
|
49
51
|
const connect = (retry = false) => {
|
|
50
52
|
if (retry) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
retries++;
|
|
54
|
+
if (retries < 5) {
|
|
55
|
+
logger.log("Reconnecting...");
|
|
56
|
+
setTimeout(connect, 1e3);
|
|
57
|
+
} else {
|
|
58
|
+
logger.warn("Giving up!");
|
|
59
|
+
}
|
|
53
60
|
return;
|
|
54
61
|
}
|
|
55
62
|
if (ws) {
|
|
@@ -59,10 +66,9 @@ export function createWebSocket() {
|
|
|
59
66
|
}
|
|
60
67
|
ws = void 0;
|
|
61
68
|
}
|
|
62
|
-
const url = useRuntimeConfig().public.sockets?.wsUrl;
|
|
63
69
|
if (url) {
|
|
64
70
|
const wsUrl = `${url}ws`;
|
|
65
|
-
logger.log(`
|
|
71
|
+
logger.log(`WS connect to ${wsUrl}`);
|
|
66
72
|
ws = new WebSocket(wsUrl);
|
|
67
73
|
ws.onopen = onOpen;
|
|
68
74
|
ws.onmessage = onMessage;
|
|
@@ -74,6 +80,7 @@ export function createWebSocket() {
|
|
|
74
80
|
connect();
|
|
75
81
|
}
|
|
76
82
|
return {
|
|
83
|
+
connect,
|
|
77
84
|
send,
|
|
78
85
|
addHandler(callback) {
|
|
79
86
|
if (typeof callback === "function") {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a style string by passing multiple independent expressions
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildStyle(...expr: string[]): string;
|
|
5
|
+
/**
|
|
6
|
+
* Build a query string by passing multiple independent expressions
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildQuery(...expr: string[]): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function buildStyle(...expr) {
|
|
2
|
+
return expr.map((expr2) => expr2.replace(/^[; ]+|[; ]+$/g, "")).filter((s) => s).join(";").replace(/\s*;\s*/g, "; ") + ";";
|
|
3
|
+
}
|
|
4
|
+
export function buildQuery(...expr) {
|
|
5
|
+
const output = expr.map((expr2) => expr2.replace(/^[?&]+|&+$/g, "")).filter((s) => s);
|
|
6
|
+
if (output.length) {
|
|
7
|
+
const [first, ...rest] = output;
|
|
8
|
+
const isParam = (expr2) => /^[^?]+=[^=]+$/.test(expr2);
|
|
9
|
+
return !isParam(first) ? rest.length > 0 ? first + (first.includes("?") ? "&" : "?") + rest.join("&") : first : "?" + output.join("&");
|
|
10
|
+
}
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses the query string from a path
|
|
3
|
+
*/
|
|
4
|
+
export declare function parseQuery(path: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Removes the query string from a path
|
|
7
|
+
*/
|
|
8
|
+
export declare function removeQuery(path: string): string;
|
|
1
9
|
/**
|
|
2
10
|
* Test path to be relative
|
|
3
11
|
*/
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import Path from "path";
|
|
2
2
|
import { extensions } from "../options.mjs";
|
|
3
|
+
export function parseQuery(path) {
|
|
4
|
+
const matches = path.match(/\?.+$/);
|
|
5
|
+
return matches ? matches[0] : "";
|
|
6
|
+
}
|
|
7
|
+
export function removeQuery(path) {
|
|
8
|
+
return path.replace(/\?.*$/, "");
|
|
9
|
+
}
|
|
3
10
|
export function isRelative(path) {
|
|
4
11
|
return !(path.startsWith("http") || Path.isAbsolute(path));
|
|
5
12
|
}
|
|
@@ -11,7 +18,7 @@ export function isImage(path) {
|
|
|
11
18
|
return extensions.image.includes(ext);
|
|
12
19
|
}
|
|
13
20
|
export function isArticle(path) {
|
|
14
|
-
return path.endsWith(".md");
|
|
21
|
+
return removeQuery(path).endsWith(".md");
|
|
15
22
|
}
|
|
16
23
|
export function isAsset(path) {
|
|
17
24
|
return !isArticle(path);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-content-assets",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Enable locally-located assets in Nuxt Content",
|
|
5
5
|
"repository": "davestewart/nuxt-content-assets",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@nuxt/kit": "^3.3.2",
|
|
35
35
|
"debounce": "^1.2.1",
|
|
36
|
-
"glob": "^9.3.2",
|
|
37
36
|
"image-size": "^1.0.2",
|
|
38
37
|
"listhen": "^1.0.4",
|
|
39
38
|
"ohash": "^1.0.0",
|