nuxt-content-assets 1.0.0 → 1.1.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/README.md +51 -26
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +11 -29
- package/dist/runtime/plugin.mjs +32 -17
- package/dist/runtime/services/assets.d.ts +3 -10
- package/dist/runtime/services/assets.mjs +4 -23
- 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
|
@@ -47,32 +47,40 @@ At build time the module [collates and serves](#how-it-works) assets and content
|
|
|
47
47
|
|
|
48
48
|
### Features
|
|
49
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
|
+
|
|
50
52
|
User experience:
|
|
51
53
|
|
|
52
54
|
- co-locate assets with content files
|
|
53
|
-
-
|
|
55
|
+
- reference assets using relative paths
|
|
56
|
+
- supports any format (image, video, doc)
|
|
54
57
|
|
|
55
58
|
Developer experience:
|
|
56
59
|
|
|
57
|
-
- works
|
|
60
|
+
- works with tags and custom components
|
|
58
61
|
- works in markdown and frontmatter
|
|
59
|
-
- file watching and live
|
|
62
|
+
- file watching and asset live-reload
|
|
60
63
|
- image size injection
|
|
61
64
|
- zero config
|
|
62
65
|
|
|
63
66
|
## Demo
|
|
64
67
|
|
|
65
|
-
To
|
|
68
|
+
To clone and run the demo locally:
|
|
66
69
|
|
|
67
|
-
```
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/davestewart/nuxt-content-assets.git
|
|
72
|
+
cd nuxt-content-assets
|
|
73
|
+
npm install
|
|
68
74
|
npm run dev
|
|
69
75
|
```
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
Then open the demo in your browser at <a href="http://localhost:3000" target="_blank">localhost:3000</a>.
|
|
78
|
+
|
|
79
|
+
To run the demo online, visit:
|
|
72
80
|
|
|
73
81
|
- https://stackblitz.com/github/davestewart/nuxt-content-assets?file=demo%2Fapp.vue
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
To browse the demo folder:
|
|
76
84
|
|
|
77
85
|
- https://github.com/davestewart/nuxt-content-assets/tree/main/demo
|
|
78
86
|
|
|
@@ -147,17 +155,23 @@ If you edit an image, video, embed or iframe source, the content will update imm
|
|
|
147
155
|
|
|
148
156
|
### Image sizing
|
|
149
157
|
|
|
150
|
-
|
|
158
|
+
#### HTML
|
|
159
|
+
|
|
160
|
+
The module is [preconfigured](#image-size) to pass image size hints (by default `style`) to generated `<img>` tags:
|
|
151
161
|
|
|
152
162
|
```html
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
>
|
|
163
|
+
<!-- imageSize: 'style' -->
|
|
164
|
+
<img src="/image.jpg" style="aspect-ratio:640/480">
|
|
165
|
+
|
|
166
|
+
<!-- imageSize: 'attrs' -->
|
|
167
|
+
<img src="/image.jpg" width="640" height="480">
|
|
158
168
|
```
|
|
159
169
|
|
|
160
|
-
|
|
170
|
+
Keeping this on prevents content jumps as your page loads.
|
|
171
|
+
|
|
172
|
+
#### Prose components
|
|
173
|
+
|
|
174
|
+
If you use [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can [hook into](demo/components/temp/ProseImg.vue) image size hints via the `$attrs` property:
|
|
161
175
|
|
|
162
176
|
```vue
|
|
163
177
|
<template>
|
|
@@ -173,15 +187,25 @@ export default {
|
|
|
173
187
|
</script>
|
|
174
188
|
```
|
|
175
189
|
|
|
176
|
-
|
|
190
|
+
#### Frontmatter
|
|
177
191
|
|
|
192
|
+
If you pass [frontmatter](demo/content/advanced/gallery.md) to [custom components](demo/components/content/ContentImage.vue) set `imageSize` to `'src'` to encode values in `src`:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
:image-content{:src="image"}
|
|
178
196
|
```
|
|
179
|
-
|
|
197
|
+
|
|
198
|
+
The component will receive the size information as a query string which you can extract and apply:
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<img class="image-content" src="/image.jpg?width=640&height=480">
|
|
180
202
|
```
|
|
181
203
|
|
|
204
|
+
See demo component [here](demo/components/content/ContentImage.vue).
|
|
205
|
+
|
|
182
206
|
## Configuration
|
|
183
207
|
|
|
184
|
-
The module
|
|
208
|
+
The module has the following options:
|
|
185
209
|
|
|
186
210
|
```ts
|
|
187
211
|
// nuxt.config.ts
|
|
@@ -205,19 +229,20 @@ You can add one or more image size hints to the generated images:
|
|
|
205
229
|
|
|
206
230
|
```ts
|
|
207
231
|
{
|
|
208
|
-
imageSize: 'attrs
|
|
232
|
+
imageSize: 'style attrs src'
|
|
209
233
|
}
|
|
210
234
|
```
|
|
211
235
|
|
|
212
236
|
Pick from the following switches:
|
|
213
237
|
|
|
214
|
-
| Switch
|
|
215
|
-
|
|
216
|
-
| `style` | Adds `style="aspect-ratio:..."` to any `<img>` tag
|
|
217
|
-
| `attrs` | Adds `width` and `height` attributes to any `<img>` tag
|
|
218
|
-
| `
|
|
238
|
+
| Switch | What it does |
|
|
239
|
+
| --------- | ------------------------------------------------------------ |
|
|
240
|
+
| `'style'` | Adds `style="aspect-ratio:..."` to any `<img>` tag |
|
|
241
|
+
| `'attrs'` | Adds `width` and `height` attributes to any `<img>` tag |
|
|
242
|
+
| `'src'` | Adds `?width=...&height=...` to `src` attribute (frontmatter only) |
|
|
243
|
+
| `false` | Disable image size hints |
|
|
219
244
|
|
|
220
|
-
Note: if you add `attrs
|
|
245
|
+
Note: if you add *only* `attrs`, include the following CSS in your app:
|
|
221
246
|
|
|
222
247
|
```css
|
|
223
248
|
img {
|
|
@@ -228,6 +253,8 @@ img {
|
|
|
228
253
|
|
|
229
254
|
### Content extensions
|
|
230
255
|
|
|
256
|
+
> Generally, you shouldn't need to touch this setting
|
|
257
|
+
|
|
231
258
|
This setting tells Nuxt Content to ignore anything that is **not** one of these file extensions:
|
|
232
259
|
|
|
233
260
|
```
|
|
@@ -236,8 +263,6 @@ md csv ya?ml json
|
|
|
236
263
|
|
|
237
264
|
This way, you can use any **other** file type as an asset, without needing to explicitly configure extensions.
|
|
238
265
|
|
|
239
|
-
Generally, you shouldn't need to touch this setting.
|
|
240
|
-
|
|
241
266
|
### Debug
|
|
242
267
|
|
|
243
268
|
If you want to see what the module does as it runs, set `debug` to true:
|
package/dist/module.d.ts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -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);
|
|
@@ -116,34 +119,15 @@ function getAssetPaths(srcDir, srcAbs) {
|
|
|
116
119
|
srcAttr
|
|
117
120
|
};
|
|
118
121
|
}
|
|
119
|
-
function getAssetSizes(srcAbs
|
|
120
|
-
|
|
121
|
-
let height = void 0;
|
|
122
|
-
let ratio = void 0;
|
|
123
|
-
let query = void 0;
|
|
124
|
-
if (hints.length && isImage(srcAbs)) {
|
|
122
|
+
function getAssetSizes(srcAbs) {
|
|
123
|
+
if (isImage(srcAbs)) {
|
|
125
124
|
try {
|
|
126
|
-
|
|
127
|
-
if (hints.includes("attrs")) {
|
|
128
|
-
width = size.width;
|
|
129
|
-
height = size.height;
|
|
130
|
-
}
|
|
131
|
-
if (hints.includes("style")) {
|
|
132
|
-
ratio = `${size.width}/${size.height}`;
|
|
133
|
-
}
|
|
134
|
-
if (hints.includes("url")) {
|
|
135
|
-
query = `?width=${size.width}&height=${size.height}`;
|
|
136
|
-
}
|
|
125
|
+
return getImageSize(srcAbs);
|
|
137
126
|
} catch (err) {
|
|
138
127
|
warn(`could not read image "${srcAbs}`);
|
|
139
128
|
}
|
|
140
129
|
}
|
|
141
|
-
return {
|
|
142
|
-
width,
|
|
143
|
-
height,
|
|
144
|
-
ratio,
|
|
145
|
-
query
|
|
146
|
-
};
|
|
130
|
+
return {};
|
|
147
131
|
}
|
|
148
132
|
|
|
149
133
|
function isAssetId(id) {
|
|
@@ -392,14 +376,11 @@ const module = defineNuxtModule({
|
|
|
392
376
|
}
|
|
393
377
|
function updateAsset(src) {
|
|
394
378
|
const { srcRel, srcAttr } = getAssetPaths(publicPath, src);
|
|
395
|
-
const { width, height
|
|
379
|
+
const { width, height } = getAssetSizes(src);
|
|
396
380
|
assets[srcRel] = {
|
|
397
|
-
srcRel,
|
|
398
381
|
srcAttr,
|
|
399
382
|
width,
|
|
400
|
-
height
|
|
401
|
-
ratio,
|
|
402
|
-
query
|
|
383
|
+
height
|
|
403
384
|
};
|
|
404
385
|
saveAssets();
|
|
405
386
|
return srcAttr;
|
|
@@ -441,6 +422,7 @@ const module = defineNuxtModule({
|
|
|
441
422
|
const makeVar = (name, value) => `export const ${name} = ${JSON.stringify(value)};`;
|
|
442
423
|
const virtualConfig = [
|
|
443
424
|
makeVar("cachePath", cachePath),
|
|
425
|
+
makeVar("imageFlags", imageFlags),
|
|
444
426
|
makeVar("debug", options.debug)
|
|
445
427
|
].join("\n");
|
|
446
428
|
nuxt.hook("nitro:config", async (config) => {
|
package/dist/runtime/plugin.mjs
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import Path from "path";
|
|
2
2
|
import { visit, SKIP, CONTINUE } from "unist-util-visit";
|
|
3
|
-
import {
|
|
4
|
-
|
|
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";
|
|
15
|
+
import { cachePath, imageFlags, debug } from "#nuxt-content-assets";
|
|
5
16
|
import { makeStorage } from "./services/index.mjs";
|
|
6
17
|
async function updateAssets() {
|
|
7
18
|
assets = await storage.getItem("assets.json");
|
|
@@ -51,10 +62,12 @@ 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,
|
|
65
|
+
const { srcAttr, width, height } = getAsset(removeQuery(value));
|
|
55
66
|
if (srcAttr) {
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
const query = width && height && (imageFlags.includes("src") || imageFlags.includes("url")) ? `width=${width}&height=${height}` : "";
|
|
68
|
+
const srcUrl = query ? buildQuery(srcAttr, parseQuery(value), query) : srcAttr;
|
|
69
|
+
parent[key] = srcUrl;
|
|
70
|
+
updated.push(`meta: ${key} to "${srcUrl}"`);
|
|
58
71
|
}
|
|
59
72
|
}
|
|
60
73
|
}, filter);
|
|
@@ -72,24 +85,26 @@ const plugin = async (nitro) => {
|
|
|
72
85
|
if (typeof value !== "string") {
|
|
73
86
|
return;
|
|
74
87
|
}
|
|
75
|
-
const { srcAttr, width, height
|
|
88
|
+
const { srcAttr, width, height } = getAsset(value);
|
|
76
89
|
if (srcAttr) {
|
|
77
90
|
node.props[prop] = srcAttr;
|
|
78
91
|
if (node.tag === "img") {
|
|
79
92
|
if (width && height) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
if (imageFlags.includes("attrs")) {
|
|
94
|
+
node.props.width ||= width;
|
|
95
|
+
node.props.height ||= height;
|
|
96
|
+
}
|
|
97
|
+
if (imageFlags.includes("style")) {
|
|
98
|
+
const ratio = `${width}/${height}`;
|
|
99
|
+
if (typeof node.props.style === "string") {
|
|
100
|
+
node.props.style = buildStyle(node.props.style, `aspect-ratio: ${ratio}`);
|
|
101
|
+
} else {
|
|
102
|
+
node.props.style ||= {};
|
|
103
|
+
node.props.style.aspectRatio = ratio;
|
|
104
|
+
}
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
|
-
}
|
|
92
|
-
if (node.tag === "a") {
|
|
107
|
+
} else if (node.tag === "a") {
|
|
93
108
|
node.props.target ||= "_blank";
|
|
94
109
|
}
|
|
95
110
|
updated.push(`page: ${tag}[${prop}] to "${srcAttr}"`);
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
export type AssetConfig = {
|
|
2
|
-
id?: string;
|
|
3
|
-
srcRel: string;
|
|
4
2
|
srcAttr: string;
|
|
5
3
|
width?: number;
|
|
6
4
|
height?: number;
|
|
7
|
-
ratio?: string;
|
|
8
|
-
query?: string;
|
|
9
5
|
};
|
|
10
6
|
/**
|
|
11
7
|
* Parse asset paths from absolute path
|
|
@@ -22,11 +18,8 @@ export declare function getAssetPaths(srcDir: string, srcAbs: string): {
|
|
|
22
18
|
* Get asset image sizes
|
|
23
19
|
*
|
|
24
20
|
* @param srcAbs The absolute path to the asset itself
|
|
25
|
-
* @param hints A list of named image size hints, i.e. 'style', 'attrs', etc
|
|
26
21
|
*/
|
|
27
|
-
export declare function getAssetSizes(srcAbs: string
|
|
28
|
-
width
|
|
29
|
-
height
|
|
30
|
-
ratio: string | undefined;
|
|
31
|
-
query: string | undefined;
|
|
22
|
+
export declare function getAssetSizes(srcAbs: string): {
|
|
23
|
+
width?: number;
|
|
24
|
+
height?: number;
|
|
32
25
|
};
|
|
@@ -11,32 +11,13 @@ export function getAssetPaths(srcDir, srcAbs) {
|
|
|
11
11
|
srcAttr
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
export function getAssetSizes(srcAbs
|
|
15
|
-
|
|
16
|
-
let height = void 0;
|
|
17
|
-
let ratio = void 0;
|
|
18
|
-
let query = void 0;
|
|
19
|
-
if (hints.length && isImage(srcAbs)) {
|
|
14
|
+
export function getAssetSizes(srcAbs) {
|
|
15
|
+
if (isImage(srcAbs)) {
|
|
20
16
|
try {
|
|
21
|
-
|
|
22
|
-
if (hints.includes("attrs")) {
|
|
23
|
-
width = size.width;
|
|
24
|
-
height = size.height;
|
|
25
|
-
}
|
|
26
|
-
if (hints.includes("style")) {
|
|
27
|
-
ratio = `${size.width}/${size.height}`;
|
|
28
|
-
}
|
|
29
|
-
if (hints.includes("url")) {
|
|
30
|
-
query = `?width=${size.width}&height=${size.height}`;
|
|
31
|
-
}
|
|
17
|
+
return getImageSize(srcAbs);
|
|
32
18
|
} catch (err) {
|
|
33
19
|
warn(`could not read image "${srcAbs}`);
|
|
34
20
|
}
|
|
35
21
|
}
|
|
36
|
-
return {
|
|
37
|
-
width,
|
|
38
|
-
height,
|
|
39
|
-
ratio,
|
|
40
|
-
query
|
|
41
|
-
};
|
|
22
|
+
return {};
|
|
42
23
|
}
|
|
@@ -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": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|