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 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
- - write relative paths to see preview in editor
55
+ - reference assets using relative paths
56
+ - supports any format (image, video, doc)
54
57
 
55
58
  Developer experience:
56
59
 
57
- - works for tags and custom components
60
+ - works with tags and custom components
58
61
  - works in markdown and frontmatter
59
- - file watching and live reload
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 view the demo locally, run:
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
- To view the demo online, visit:
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
- You can browse the demo files in:
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
- You can [configure](#image-size) the module to add image size attributes to generated `<img>` tags:
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
- <img src="/image.jpg"
154
- style="aspect-ratio:640/480"
155
- width="640"
156
- height="480"
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
- If you use [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can [hook into these values](demo/components/temp/ProseImg.vue) via the `$attrs` property:
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
- If you pass [frontmatter](demo/content/advanced/gallery.md) to [custom components](demo/components/content/ContentImage.vue) set the `'url'` configuration option to encode size in the URL:
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
- :image-gallery={:data="images"}
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 can be configured in your Nuxt configuration file:
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 url'
232
+ imageSize: 'style attrs src'
209
233
  }
210
234
  ```
211
235
 
212
236
  Pick from the following switches:
213
237
 
214
- | Switch | What it does |
215
- |---------|---------------------------------------------------------------------------|
216
- | `style` | Adds `style="aspect-ratio:..."` to any `<img>` tag |
217
- | `attrs` | Adds `width` and `height` attributes to any `<img>` tag |
218
- | `url` | Adds a `?width=...&height=...` query string to image paths in frontmatter |
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` only, include the following CSS in your app:
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
@@ -1,7 +1,7 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  interface ModuleOptions {
4
- imageSize?: string | string[];
4
+ imageSize?: string | string[] | false;
5
5
  contentExtensions: string | string[];
6
6
  debug?: boolean;
7
7
  }
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "1.0.0"
7
+ "version": "1.1.1"
8
8
  }
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, hints) {
120
- let width = void 0;
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
- const size = getImageSize(srcAbs);
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, ratio, query } = getAssetSizes(src, imageFlags);
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) => {
@@ -1,7 +1,18 @@
1
1
  import Path from "path";
2
2
  import { visit, SKIP, CONTINUE } from "unist-util-visit";
3
- import { deKey, isValidAsset, list, matchTokens, toPath, walk } from "./utils/index.mjs";
4
- import { debug, cachePath } from "#nuxt-content-assets";
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, query } = getAsset(value);
65
+ const { srcAttr, width, height } = getAsset(removeQuery(value));
55
66
  if (srcAttr) {
56
- parent[key] = srcAttr + (query || "");
57
- updated.push(`meta: ${key} to "${srcAttr}"`);
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, ratio } = getAsset(value);
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
- node.props.width ||= width;
81
- node.props.height ||= height;
82
- }
83
- if (ratio) {
84
- if (typeof node.props.style === "string") {
85
- node.props.style += `; aspect-ratio: ${ratio};`;
86
- } else {
87
- node.props.style ||= {};
88
- node.props.style.aspectRatio = ratio;
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, hints: string[]): {
28
- width: number | undefined;
29
- height: number | undefined;
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, hints) {
15
- let width = void 0;
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
- const size = getImageSize(srcAbs);
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,5 +1,6 @@
1
- export * from './assert';
1
+ export * from './path';
2
2
  export * from './debug';
3
+ export * from './build';
3
4
  export * from './fs';
4
5
  export * from './string';
5
6
  export * from './object';
@@ -1,5 +1,6 @@
1
- export * from "./assert.mjs";
1
+ export * from "./path.mjs";
2
2
  export * from "./debug.mjs";
3
+ export * from "./build.mjs";
3
4
  export * from "./fs.mjs";
4
5
  export * from "./string.mjs";
5
6
  export * from "./object.mjs";
@@ -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.0.0",
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",