nuxt-content-assets 0.5.3 → 0.6.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 CHANGED
@@ -26,7 +26,7 @@ Nuxt Content Assets enables locally-located assets in your [Nuxt Content](https:
26
26
  +- seaside.mp4
27
27
  ```
28
28
 
29
- In your documents, simply reference assets using relative paths:
29
+ In your documents, reference assets with relative paths:
30
30
 
31
31
  ```markdown
32
32
  ---
@@ -43,11 +43,7 @@ Almost as much as being in the sea!
43
43
  <video src="media/seaside.mp4"></video>
44
44
  ```
45
45
 
46
- The module:
47
-
48
- - supports configurable image, media and file types
49
- - supports appropriate HTML tags
50
- - converts markdown and frontmatter content
46
+ That's it!
51
47
 
52
48
  ## Demo
53
49
 
@@ -84,26 +80,65 @@ export default defineNuxtConfig({
84
80
  })
85
81
  ```
86
82
 
87
- Run the dev server or build and assets should now be served alongside markdown content.
83
+ Run the dev server or build and local assets should now be served alongside markdown content.
88
84
 
89
85
  ## Usage
90
86
 
91
87
  ### Overview
92
88
 
93
- Once the build or dev server is running, paths should be rewritten and assets served automatically.
89
+ Use relative paths anywhere within your documents:
94
90
 
95
- Here's how it works:
91
+ ```mdx
92
+ ![image](image.jpg)
93
+ <video src="media/video.mp4" />
94
+ ```
96
95
 
97
- - the module scans content folders for assets
98
- - these are copied to a temporary build folder
99
- - matching relative paths in markdown are updated
100
- - Nitro serves the assets from the new location
96
+ Relative paths are defined by anything not starting with a slash or `http`, for example:
97
+
98
+ ```
99
+ image.jpg
100
+ images/featured.png
101
+ ../assets/cv.pdf
102
+ ```
101
103
 
102
- Right now, if you change any assets, you will need to re-run the dev server / build.
104
+ Note that only specific tags and attributes are targeted:
103
105
 
104
- ### Supported formats
106
+ ```html
107
+ <img src="...">
108
+ <video src="...">
109
+ <audio src="...">
110
+ <source src="...">
111
+ <embed src="...">
112
+ <iframe src="...">
113
+ <a href="...">
114
+ ```
115
+
116
+ However, you can use relative paths in frontmatter:
117
+
118
+ ```mdx
119
+ ---
120
+ title: Portfolio Item 1
121
+ images:
122
+ - images/image-1.jpg
123
+ - images/image-2.jpg
124
+ - images/image-3.jpg
125
+ ---
126
+ ```
105
127
 
106
- The following file formats are supported out of the box:
128
+ Then pass these to components like so:
129
+
130
+ ```markdown
131
+ ::gallery{:images="images"}
132
+ ::
133
+ ```
134
+
135
+ > Note: to pass size hints in frontmatter, set the `imageSize` configuration option to `'url'`
136
+
137
+ See the [Demo](demo/content/recipes/index.md) for an example.
138
+
139
+ ### Supported assets
140
+
141
+ The following file extensions are targeted by default:
107
142
 
108
143
  | Type | Extensions |
109
144
  |--------|-------------------------------------------------------------------------|
@@ -111,31 +146,22 @@ The following file formats are supported out of the box:
111
146
  | Media | `mp3`, `m4a`, `wav`, `mp4`, `mov`, `webm`, `ogg`, `avi`, `flv`, `avchd` |
112
147
  | Files | `pdf`, `doc`, `docx`, `xls`, `xlsx`, `ppt`, `pptx`, `odp`, `key` |
113
148
 
114
- See the [configuration](#output) section for more options.
149
+ See the [configuration](#output) section to modify or change this list, and the [Demo](demo/nuxt.config.ts) for an example.
115
150
 
116
151
  ### Images
117
152
 
118
- The module can [optionally](#image-attributes) write `width`, `height` and `aspect-ratio` information to generated `<img>` tags:
153
+ The module can prevent content jumps by optionally writing image size information to generated `<img>` tags:
119
154
 
120
155
  ```html
121
- <img src="..." width="640" height="480" style="aspect-ratio:640/480">
122
- ```
123
-
124
- This can prevent content jumps on page load. If you add `attributes` only, include the following CSS in your app:
125
-
126
- ```css
127
- img {
128
- max-width: 100%;
129
- height: auto;
130
- }
156
+ <img src="/image.jpg?width=640&height=480" width="640" height="480" style="aspect-ratio:640/480">
131
157
  ```
132
158
 
133
- If you use custom [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can use these values in your own markup:
159
+ If you use custom [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can pass these values to your own markup:
134
160
 
135
161
  ```vue
136
162
  <template>
137
163
  <span class="image">
138
- <img :src="$attrs.src" :style="$attrs.style" />
164
+ <img :src="$attrs.src" :width="$attrs.width" :height="$attrs.height" />
139
165
  </span>
140
166
  </template>
141
167
 
@@ -146,11 +172,22 @@ export default {
146
172
  </script>
147
173
  ```
148
174
 
149
- See the [Demo](demo/components/_content/ProseImg.vue) for an example.
175
+ See the [configuration](#image-size) section to add this, and the [Demo](demo/components/_content/ProseImg.vue) for an example.
176
+
177
+ ### Build
178
+
179
+ Once the dev server or build is running, the following happens:
180
+
181
+ - the module scans content folders for assets
182
+ - these are copied to a temporary build folder
183
+ - matching relative paths markdown are rewritten
184
+ - Nitro serves the assets from the new location
185
+
186
+ If you change any assets, you'll need to re-run the dev server / build (there is an [issue](https://github.com/davestewart/nuxt-content-assets/issues/1) open to look at this).
150
187
 
151
188
  ## Configuration
152
189
 
153
- The module **doesn't require** any configuration, but you can tweak the following settings:
190
+ The module will run happily without configuration, but should you need to:
154
191
 
155
192
  ```ts
156
193
  // nuxt.config.ts
@@ -166,7 +203,7 @@ export default defineNuxtConfig({
166
203
  extensions: 'png jpg',
167
204
 
168
205
  // use aspect-ratio rather than attributes
169
- imageSize: 'ratio',
206
+ imageSize: 'style',
170
207
 
171
208
  // print debug messages to the console
172
209
  debug: true,
@@ -233,9 +270,9 @@ To replace extensions, use `extensions`:
233
270
  }
234
271
  ```
235
272
 
236
- ### Image attributes
273
+ ### Image size
237
274
 
238
- You can add image size hints to the generated images.
275
+ You can add image size hints to the generated images via attributes, style, or the URL.
239
276
 
240
277
  To add `style` aspect-ratio:
241
278
 
@@ -253,7 +290,34 @@ To add `width` and `height` attributes:
253
290
  }
254
291
  ```
255
292
 
256
- You can even add both if you need to.
293
+ If you add `attributes` only, include the following CSS in your app:
294
+
295
+ ```css
296
+ img {
297
+ max-width: 100%;
298
+ height: auto;
299
+ }
300
+ ```
301
+
302
+ To pass size hints as parameters in the URL (frontmatter only, see [Demo](demo/components/content/Gallery.vue)):
303
+
304
+ ```ts
305
+ {
306
+ imageSize: 'url'
307
+ }
308
+ ```
309
+
310
+ Note that you can one, some or all keywords, i.e. `attrs url`.
311
+
312
+ ### Debug
313
+
314
+ If you want to see what the module does as it runs, set `debug` to true:
315
+
316
+ ```ts
317
+ {
318
+ debug: true
319
+ }
320
+ ```
257
321
 
258
322
  ## Development
259
323
 
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.5.3"
7
+ "version": "0.6.0"
8
8
  }
package/dist/module.mjs CHANGED
@@ -6,17 +6,8 @@ import * as Path from 'path';
6
6
  import Path__default from 'path';
7
7
  import { hash } from 'ohash';
8
8
 
9
- function getSources(sources) {
10
- return Object.keys(sources).reduce((output, key) => {
11
- const source = sources[key];
12
- if (source) {
13
- const { driver, base } = source;
14
- if (driver === "fs") {
15
- output[key] = base;
16
- }
17
- }
18
- return output;
19
- }, {});
9
+ function matchWords(value) {
10
+ return typeof value === "string" ? value.match(/\w+/g) || [] : [];
20
11
  }
21
12
 
22
13
  const defaults = {
@@ -32,6 +23,24 @@ const extensions = [
32
23
  ...fileExtensions
33
24
  ];
34
25
 
26
+ function isImage(path) {
27
+ const ext = Path__default.extname(path).substring(1);
28
+ return imageExtensions.includes(ext);
29
+ }
30
+
31
+ function getSources(sources) {
32
+ return Object.keys(sources).reduce((output, key) => {
33
+ const source = sources[key];
34
+ if (source) {
35
+ const { driver, base } = source;
36
+ if (driver === "fs") {
37
+ output[key] = base;
38
+ }
39
+ }
40
+ return output;
41
+ }, {});
42
+ }
43
+
35
44
  const moduleName = "nuxt-content-assets";
36
45
  const moduleKey = "content-assets";
37
46
 
@@ -39,13 +48,14 @@ function log(...data) {
39
48
  console.info(`[${moduleKey}]`, ...data);
40
49
  }
41
50
 
42
- function matchWords(value) {
43
- return value.match(/\w+/g) || [];
44
- }
45
- function isImage(path) {
46
- const ext = Path__default.extname(path).substring(1);
47
- return imageExtensions.includes(ext);
48
- }
51
+ const replacers = {
52
+ folder: (src, dir) => Path__default.dirname(src.replace(dir, "")),
53
+ file: (src) => Path__default.basename(src),
54
+ name: (src) => Path__default.basename(src, Path__default.extname(src)),
55
+ extname: (src) => Path__default.extname(src),
56
+ ext: (src) => Path__default.extname(src).substring(1),
57
+ hash: (src) => hash({ src })
58
+ };
49
59
  function interpolatePattern(pattern, src, dir, warn = false) {
50
60
  return Path__default.join(pattern.replace(/\[\w+]/g, (match) => {
51
61
  const name = match.substring(1, match.length - 1);
@@ -59,14 +69,6 @@ function interpolatePattern(pattern, src, dir, warn = false) {
59
69
  return match;
60
70
  }));
61
71
  }
62
- const replacers = {
63
- folder: (src, dir) => Path__default.dirname(src.replace(dir, "")),
64
- file: (src) => Path__default.basename(src),
65
- name: (src) => Path__default.basename(src, Path__default.extname(src)),
66
- extname: (src) => Path__default.extname(src),
67
- ext: (src) => Path__default.extname(src).substring(1),
68
- hash: (src) => hash({ src })
69
- };
70
72
 
71
73
  const resolve = createResolver(import.meta.url).resolve;
72
74
  const module = defineNuxtModule({
@@ -111,37 +113,42 @@ const module = defineNuxtModule({
111
113
  const assetsDir = matches ? matches[1] : defaults.assetsDir;
112
114
  const assetsPattern = (matches ? matches[2] : "") || defaults.assetsPattern;
113
115
  interpolatePattern(assetsPattern, "", "", true);
116
+ const imageFlags = matchWords(options.imageSize);
114
117
  if (options.extensions?.trim()) {
115
118
  extensions.splice(0, extensions.length, ...matchWords(options.extensions));
116
119
  } else if (options.additionalExtensions) {
117
120
  extensions.push(...matchWords(options.additionalExtensions));
118
121
  }
119
- if (nuxt.options.content) {
120
- (_a = nuxt.options.content).ignores || (_a.ignores = []);
121
- }
122
122
  function getAssetConfig(pattern, src, dir) {
123
123
  let width = void 0;
124
124
  let height = void 0;
125
- let ratio = "";
126
- if (options.imageSize && isImage(src)) {
125
+ let ratio = void 0;
126
+ let query = void 0;
127
+ if (imageFlags.length && isImage(src)) {
127
128
  const size = getImageSize(src);
128
- if (options.imageSize.includes("style")) {
129
+ if (imageFlags.includes("style")) {
129
130
  ratio = `${size.width}/${size.height}`;
130
131
  }
131
- if (options.imageSize.includes("attrs")) {
132
+ if (imageFlags.includes("attrs")) {
132
133
  width = size.width;
133
134
  height = size.height;
134
135
  }
136
+ if (imageFlags.includes("url")) {
137
+ query = `?width=${width}&height=${height}`;
138
+ }
135
139
  }
136
140
  const id = Path.join(Path.basename(dir), Path.relative(dir, src)).replaceAll("/", ":");
137
141
  const file = interpolatePattern(pattern, src, dir);
138
142
  const trg = Path.join(cachePath, assetsDir, file);
139
143
  const rel = Path.join("/", assetsDir, file);
140
- return { id, file, trg, rel, width, height, ratio };
144
+ return { id, file, trg, rel, width, height, ratio, query };
141
145
  }
142
146
  const publicFolder = Path.join(cachePath, assetsDir);
143
147
  const sourceFolders = Object.values(sources);
144
148
  const assets = {};
149
+ if (nuxt.options.content) {
150
+ (_a = nuxt.options.content).ignores || (_a.ignores = []);
151
+ }
145
152
  sourceFolders.forEach((folder) => {
146
153
  const pattern = `${folder}/**/*.{${extensions.join(",")}}`;
147
154
  const paths = glob.globSync(pattern);
@@ -2,8 +2,8 @@ export declare const defaults: {
2
2
  assetsDir: string;
3
3
  assetsPattern: string;
4
4
  };
5
- export declare const imageExtensions: RegExpMatchArray | [];
6
- export declare const mediaExtensions: RegExpMatchArray | [];
7
- export declare const fileExtensions: RegExpMatchArray | [];
5
+ export declare const imageExtensions: string[];
6
+ export declare const mediaExtensions: string[];
7
+ export declare const fileExtensions: string[];
8
8
  export declare const extensions: string[];
9
9
  export declare const tags: string[];
@@ -1,4 +1,4 @@
1
- import { matchWords } from "./utils/assets.mjs";
1
+ import { matchWords } from "./utils/config.mjs";
2
2
  export const defaults = {
3
3
  assetsDir: "assets/content",
4
4
  assetsPattern: "[folder]/[file]"
@@ -21,9 +21,9 @@ export default defineNitroPlugin(async (nitroApp) => {
21
21
  const filter = (value, key) => !(String(key).startsWith("_") || key === "body");
22
22
  walk(file, (value, parent, key) => {
23
23
  if (isValidAsset(value)) {
24
- const { rel } = getAsset(absDoc, value);
24
+ const { rel, query } = getAsset(absDoc, value);
25
25
  if (rel) {
26
- parent[key] = rel;
26
+ parent[key] = rel + (query || "");
27
27
  }
28
28
  }
29
29
  }, filter);
@@ -1,7 +1,3 @@
1
- /**
2
- * Get matched words from a string
3
- */
4
- export declare function matchWords(value: string): RegExpMatchArray | [];
5
1
  /**
6
2
  * Test path to be relative
7
3
  */
@@ -18,16 +14,3 @@ export declare function isAsset(path: string): boolean;
18
14
  * Test if value is a valid asset
19
15
  */
20
16
  export declare function isValidAsset(value?: string): boolean;
21
- /**
22
- * Interpolate assets path pattern
23
- *
24
- * @param pattern A path pattern with tokens
25
- * @param src The absolute path to a src asset
26
- * @param dir The absolute path to its containing folder
27
- * @param warn An optional flag to warn for unknown tokens
28
- */
29
- export declare function interpolatePattern(pattern: string, src: string, dir: string, warn?: boolean): any;
30
- /**
31
- * Hash of replacer functions
32
- */
33
- export declare const replacers: Record<string, Function>;
@@ -1,10 +1,5 @@
1
1
  import Path from "path";
2
- import { hash as ohash } from "ohash";
3
2
  import { extensions, imageExtensions } from "../options.mjs";
4
- import { log } from "./debug.mjs";
5
- export function matchWords(value) {
6
- return value.match(/\w+/g) || [];
7
- }
8
3
  export function isRelative(path) {
9
4
  return !(path.startsWith("http") || Path.isAbsolute(path));
10
5
  }
@@ -19,24 +14,3 @@ export function isAsset(path) {
19
14
  export function isValidAsset(value) {
20
15
  return typeof value === "string" && isAsset(value) && isRelative(value);
21
16
  }
22
- export function interpolatePattern(pattern, src, dir, warn = false) {
23
- return Path.join(pattern.replace(/\[\w+]/g, (match) => {
24
- const name = match.substring(1, match.length - 1);
25
- const fn = replacers[name];
26
- if (fn) {
27
- return fn(src, dir);
28
- }
29
- if (warn) {
30
- log(`Unknown output token ${match}`, true);
31
- }
32
- return match;
33
- }));
34
- }
35
- export const replacers = {
36
- folder: (src, dir) => Path.dirname(src.replace(dir, "")),
37
- file: (src) => Path.basename(src),
38
- name: (src) => Path.basename(src, Path.extname(src)),
39
- extname: (src) => Path.extname(src),
40
- ext: (src) => Path.extname(src).substring(1),
41
- hash: (src) => ohash({ src })
42
- };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Get matched words from a string
3
+ */
4
+ export declare function matchWords(value?: string): string[];
@@ -0,0 +1,3 @@
1
+ export function matchWords(value) {
2
+ return typeof value === "string" ? value.match(/\w+/g) || [] : [];
3
+ }
@@ -1,2 +1,3 @@
1
1
  export declare function log(...data: any[]): void;
2
2
  export declare function warn(...data: any[]): void;
3
+ export declare function dump(name: string, data: any): void;
@@ -1,3 +1,5 @@
1
+ import Path from "path";
2
+ import Fs from "fs";
1
3
  import { moduleKey } from "../../config";
2
4
  export function log(...data) {
3
5
  console.info(`[${moduleKey}]`, ...data);
@@ -5,3 +7,10 @@ export function log(...data) {
5
7
  export function warn(...data) {
6
8
  console.warn(`[${moduleKey}]`, ...data);
7
9
  }
10
+ export function dump(name, data) {
11
+ const path = `debug/${name}.json`;
12
+ const folder = Path.dirname(path);
13
+ log(`Dumping "${path}"`);
14
+ Fs.mkdirSync(folder, { recursive: true });
15
+ Fs.writeFileSync(path, JSON.stringify(data, null, " "), { encoding: "utf8" });
16
+ }
@@ -1,4 +1,6 @@
1
- export * from './content';
2
- export * from './object';
3
1
  export * from './assets';
2
+ export * from './config';
3
+ export * from './content';
4
4
  export * from './debug';
5
+ export * from './object';
6
+ export * from './paths';
@@ -1,4 +1,6 @@
1
- export * from "./content.mjs";
2
- export * from "./object.mjs";
3
1
  export * from "./assets.mjs";
2
+ export * from "./config.mjs";
3
+ export * from "./content.mjs";
4
4
  export * from "./debug.mjs";
5
+ export * from "./object.mjs";
6
+ export * from "./paths.mjs";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Interpolate assets path pattern
3
+ *
4
+ * @param pattern A path pattern with tokens
5
+ * @param src The absolute path to a src asset
6
+ * @param dir The absolute path to its containing folder
7
+ * @param warn An optional flag to warn for unknown tokens
8
+ */
9
+ export declare function interpolatePattern(pattern: string, src: string, dir: string, warn?: boolean): string;
@@ -0,0 +1,24 @@
1
+ import Path from "path";
2
+ import { hash } from "ohash";
3
+ import { log } from "./debug.mjs";
4
+ const replacers = {
5
+ folder: (src, dir) => Path.dirname(src.replace(dir, "")),
6
+ file: (src) => Path.basename(src),
7
+ name: (src) => Path.basename(src, Path.extname(src)),
8
+ extname: (src) => Path.extname(src),
9
+ ext: (src) => Path.extname(src).substring(1),
10
+ hash: (src) => hash({ src })
11
+ };
12
+ export function interpolatePattern(pattern, src, dir, warn = false) {
13
+ return Path.join(pattern.replace(/\[\w+]/g, (match) => {
14
+ const name = match.substring(1, match.length - 1);
15
+ const fn = replacers[name];
16
+ if (fn) {
17
+ return fn(src, dir);
18
+ }
19
+ if (warn) {
20
+ log(`Unknown output token ${match}`, true);
21
+ }
22
+ return match;
23
+ }));
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-content-assets",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Enable locally-located assets in Nuxt Content",
5
5
  "repository": "davestewart/nuxt-content-assets",
6
6
  "license": "MIT",
@@ -18,11 +18,13 @@
18
18
  "dist"
19
19
  ],
20
20
  "scripts": {
21
- "prepack": "nuxt-module-build",
22
21
  "dev": "nuxi dev demo",
23
22
  "dev:build": "nuxi build demo",
24
23
  "dev:prepare": "nuxt-module-build --stub && nuxi prepare demo",
25
- "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
24
+ "build": "nuxt-module-build",
25
+ "release": "npm run lint && npm run test && npm run build && npm publish && git push --follow-tags",
26
+ "release:dry": "npm run lint && npm run test && npm run build && npm publish --dry-run",
27
+ "release:old": "npm run lint && npm run test && npm run build && changelogen --release && npm publish && git push --follow-tags",
26
28
  "lint": "eslint .",
27
29
  "test": "vitest run",
28
30
  "test:watch": "vitest watch"
@@ -48,4 +50,4 @@
48
50
  "nuxt": "^3.3.2",
49
51
  "vitest": "^0.29.7"
50
52
  }
51
- }
53
+ }