nuxt-content-assets 0.5.2 → 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
@@ -1,12 +1,16 @@
1
1
  # Nuxt Content Assets
2
2
 
3
- > Enable locally-located assets in Nuxt Content
4
-
5
3
  [![npm version][npm-version-src]][npm-version-href]
6
4
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
5
  [![License][license-src]][license-href]
8
6
  [![Nuxt][nuxt-src]][nuxt-href]
9
7
 
8
+ > Enable locally-located assets in Nuxt Content
9
+
10
+ <p align="center">
11
+ <img src="https://raw.githubusercontent.com/davestewart/nuxt-content-assets/main/demo/content/splash.png" alt="Nuxt Content Assets logo">
12
+ </p>
13
+
10
14
  ## Overview
11
15
 
12
16
  Nuxt Content Assets enables locally-located assets in your [Nuxt Content](https://content.nuxtjs.org/) folder:
@@ -22,7 +26,7 @@ Nuxt Content Assets enables locally-located assets in your [Nuxt Content](https:
22
26
  +- seaside.mp4
23
27
  ```
24
28
 
25
- In your documents, simply reference assets using relative paths:
29
+ In your documents, reference assets with relative paths:
26
30
 
27
31
  ```markdown
28
32
  ---
@@ -39,11 +43,7 @@ Almost as much as being in the sea!
39
43
  <video src="media/seaside.mp4"></video>
40
44
  ```
41
45
 
42
- The module:
43
-
44
- - supports configurable image, media and file types
45
- - supports appropriate HTML tags
46
- - converts markdown and frontmatter content
46
+ That's it!
47
47
 
48
48
  ## Demo
49
49
 
@@ -80,26 +80,65 @@ export default defineNuxtConfig({
80
80
  })
81
81
  ```
82
82
 
83
- 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.
84
84
 
85
85
  ## Usage
86
86
 
87
87
  ### Overview
88
88
 
89
- Once the build or dev server is running, paths should be rewritten and assets served automatically.
89
+ Use relative paths anywhere within your documents:
90
90
 
91
- Here's how it works:
91
+ ```mdx
92
+ ![image](image.jpg)
93
+ <video src="media/video.mp4" />
94
+ ```
92
95
 
93
- - the module scans content folders for assets
94
- - these are copied to a temporary build folder
95
- - matching relative paths in markdown are updated
96
- - 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
+ ```
103
+
104
+ Note that only specific tags and attributes are targeted:
105
+
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
+ ```
127
+
128
+ Then pass these to components like so:
129
+
130
+ ```markdown
131
+ ::gallery{:images="images"}
132
+ ::
133
+ ```
97
134
 
98
- Right now, if you change any assets, you will need to re-run the dev server / build.
135
+ > Note: to pass size hints in frontmatter, set the `imageSize` configuration option to `'url'`
99
136
 
100
- ### Supported formats
137
+ See the [Demo](demo/content/recipes/index.md) for an example.
101
138
 
102
- The following file formats are supported out of the box:
139
+ ### Supported assets
140
+
141
+ The following file extensions are targeted by default:
103
142
 
104
143
  | Type | Extensions |
105
144
  |--------|-------------------------------------------------------------------------|
@@ -107,31 +146,22 @@ The following file formats are supported out of the box:
107
146
  | Media | `mp3`, `m4a`, `wav`, `mp4`, `mov`, `webm`, `ogg`, `avi`, `flv`, `avchd` |
108
147
  | Files | `pdf`, `doc`, `docx`, `xls`, `xlsx`, `ppt`, `pptx`, `odp`, `key` |
109
148
 
110
- 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.
111
150
 
112
151
  ### Images
113
152
 
114
- 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:
115
154
 
116
155
  ```html
117
- <img src="..." width="640" height="480" style="aspect-ratio:640/480">
156
+ <img src="/image.jpg?width=640&height=480" width="640" height="480" style="aspect-ratio:640/480">
118
157
  ```
119
158
 
120
- This can prevent content jumps on page load. If you add `attributes` only, include the following CSS in your app:
121
-
122
- ```css
123
- img {
124
- max-width: 100%;
125
- height: auto;
126
- }
127
- ```
128
-
129
- 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:
130
160
 
131
161
  ```vue
132
162
  <template>
133
163
  <span class="image">
134
- <img :src="$attrs.src" :style="$attrs.style" />
164
+ <img :src="$attrs.src" :width="$attrs.width" :height="$attrs.height" />
135
165
  </span>
136
166
  </template>
137
167
 
@@ -142,11 +172,22 @@ export default {
142
172
  </script>
143
173
  ```
144
174
 
145
- 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).
146
187
 
147
188
  ## Configuration
148
189
 
149
- 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:
150
191
 
151
192
  ```ts
152
193
  // nuxt.config.ts
@@ -162,7 +203,7 @@ export default defineNuxtConfig({
162
203
  extensions: 'png jpg',
163
204
 
164
205
  // use aspect-ratio rather than attributes
165
- imageSize: 'ratio',
206
+ imageSize: 'style',
166
207
 
167
208
  // print debug messages to the console
168
209
  debug: true,
@@ -229,9 +270,9 @@ To replace extensions, use `extensions`:
229
270
  }
230
271
  ```
231
272
 
232
- ### Image attributes
273
+ ### Image size
233
274
 
234
- 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.
235
276
 
236
277
  To add `style` aspect-ratio:
237
278
 
@@ -249,7 +290,34 @@ To add `width` and `height` attributes:
249
290
  }
250
291
  ```
251
292
 
252
- 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
+ ```
253
321
 
254
322
  ## Development
255
323
 
package/dist/module.json CHANGED
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "name": "nuxt-content-assets",
3
3
  "configKey": "content-assets",
4
- "version": "0.5.2"
4
+ "compatibility": {
5
+ "nuxt": "^3.0.0"
6
+ },
7
+ "version": "0.6.0"
5
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,20 +69,15 @@ 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({
73
75
  meta: {
74
76
  name: moduleName,
75
- configKey: moduleKey
77
+ configKey: moduleKey,
78
+ compatibility: {
79
+ nuxt: "^3.0.0"
80
+ }
76
81
  },
77
82
  defaults: {
78
83
  output: `${defaults.assetsDir}/${defaults.assetsPattern}`,
@@ -108,37 +113,42 @@ const module = defineNuxtModule({
108
113
  const assetsDir = matches ? matches[1] : defaults.assetsDir;
109
114
  const assetsPattern = (matches ? matches[2] : "") || defaults.assetsPattern;
110
115
  interpolatePattern(assetsPattern, "", "", true);
116
+ const imageFlags = matchWords(options.imageSize);
111
117
  if (options.extensions?.trim()) {
112
118
  extensions.splice(0, extensions.length, ...matchWords(options.extensions));
113
119
  } else if (options.additionalExtensions) {
114
120
  extensions.push(...matchWords(options.additionalExtensions));
115
121
  }
116
- if (nuxt.options.content) {
117
- (_a = nuxt.options.content).ignores || (_a.ignores = []);
118
- }
119
122
  function getAssetConfig(pattern, src, dir) {
120
123
  let width = void 0;
121
124
  let height = void 0;
122
- let ratio = "";
123
- if (options.imageSize && isImage(src)) {
125
+ let ratio = void 0;
126
+ let query = void 0;
127
+ if (imageFlags.length && isImage(src)) {
124
128
  const size = getImageSize(src);
125
- if (options.imageSize.includes("style")) {
129
+ if (imageFlags.includes("style")) {
126
130
  ratio = `${size.width}/${size.height}`;
127
131
  }
128
- if (options.imageSize.includes("attrs")) {
132
+ if (imageFlags.includes("attrs")) {
129
133
  width = size.width;
130
134
  height = size.height;
131
135
  }
136
+ if (imageFlags.includes("url")) {
137
+ query = `?width=${width}&height=${height}`;
138
+ }
132
139
  }
133
140
  const id = Path.join(Path.basename(dir), Path.relative(dir, src)).replaceAll("/", ":");
134
141
  const file = interpolatePattern(pattern, src, dir);
135
142
  const trg = Path.join(cachePath, assetsDir, file);
136
143
  const rel = Path.join("/", assetsDir, file);
137
- return { id, file, trg, rel, width, height, ratio };
144
+ return { id, file, trg, rel, width, height, ratio, query };
138
145
  }
139
146
  const publicFolder = Path.join(cachePath, assetsDir);
140
147
  const sourceFolders = Object.values(sources);
141
148
  const assets = {};
149
+ if (nuxt.options.content) {
150
+ (_a = nuxt.options.content).ignores || (_a.ignores = []);
151
+ }
142
152
  sourceFolders.forEach((folder) => {
143
153
  const pattern = `${folder}/**/*.{${extensions.join(",")}}`;
144
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.2",
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
+ }