nuxt-content-assets 0.5.1 → 0.5.3

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:
@@ -53,7 +57,7 @@ To run the demo online, go to:
53
57
 
54
58
  You can browse the demo files in:
55
59
 
56
- - .https://github.com/davestewart/nuxt-content-assets/tree/main/demo
60
+ - https://github.com/davestewart/nuxt-content-assets/tree/main/demo
57
61
 
58
62
  To run the demo locally, clone the application and from the root, run:
59
63
 
@@ -74,7 +78,7 @@ Configure `nuxt.config.ts`:
74
78
  ```js
75
79
  export default defineNuxtConfig({
76
80
  modules: [
77
- 'nuxt-content-relative-assets', // make sure to add before content!
81
+ 'nuxt-content-assets', // make sure to add before content!
78
82
  '@nuxt/content',
79
83
  ]
80
84
  })
@@ -111,21 +115,28 @@ See the [configuration](#output) section for more options.
111
115
 
112
116
  ### Images
113
117
 
114
- The module [optionally](#image-attributes) writes `width` and `height` attributes to the generated HTML:
118
+ The module can [optionally](#image-attributes) write `width`, `height` and `aspect-ratio` information to generated `<img>` tags:
115
119
 
116
120
  ```html
117
- <img src="..." width="640" height="480">
121
+ <img src="..." width="640" height="480" style="aspect-ratio:640/480">
118
122
  ```
119
123
 
120
- This locks the aspect ratio of the image preventing content jumps.
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
+ }
131
+ ```
121
132
 
122
- If you use custom [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can even grab these values using the Vue `$attrs` property:
133
+ If you use custom [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can use these values in your own markup:
123
134
 
124
135
  ```vue
125
136
  <template>
126
- <div class="image">
127
- <img :src="$attrs.src" :style="`aspect-ratio:${$attrs.width}/${$attrs.height}`" />
128
- </div>
137
+ <span class="image">
138
+ <img :src="$attrs.src" :style="$attrs.style" />
139
+ </span>
129
140
  </template>
130
141
 
131
142
  <script>
@@ -154,8 +165,8 @@ export default defineNuxtConfig({
154
165
  // completely replace supported extensions
155
166
  extensions: 'png jpg',
156
167
 
157
- // add image width and height
158
- imageAttrs: true,
168
+ // use aspect-ratio rather than attributes
169
+ imageSize: 'ratio',
159
170
 
160
171
  // print debug messages to the console
161
172
  debug: true,
@@ -168,7 +179,7 @@ export default defineNuxtConfig({
168
179
  The output path can be customised using a template string:
169
180
 
170
181
  ```
171
- assets/img/content/[path]/[name][extname]
182
+ assets/content/[name]-[hash].[ext]
172
183
  ```
173
184
 
174
185
  The first part of the path should be public root-relative folder:
@@ -177,7 +188,7 @@ The first part of the path should be public root-relative folder:
177
188
  assets/img/content
178
189
  ```
179
190
 
180
- The optional second part of the path indicates specific placement of each image:
191
+ The optional second part of the path indicates the relative location of each image:
181
192
 
182
193
  | Token | Description | Example |
183
194
  |-------------|--------------------------------------------|--------------------|
@@ -196,15 +207,15 @@ For example:
196
207
  | `assets/img/[name]-[hash].[ext]` | `assets/img/featured-9M00N4l9A0.jpg` |
197
208
  | `content/[hash].[ext]` | `content/9M00N4l9A0.jpg` |
198
209
 
199
- Note that the module defaults to all files in a single folder:
210
+ Note that the module defaults to:
200
211
 
201
212
  ```
202
- /assets/content/[name]-[hash].[ext]
213
+ /assets/content/[folder]/[file]
203
214
  ```
204
215
 
205
216
  ### Extensions
206
217
 
207
- You can add or replace supported extensions if you need to:
218
+ You can add (or replace) supported extensions if you need to:
208
219
 
209
220
  To add extensions, use `additionalExtensions`:
210
221
 
@@ -214,26 +225,36 @@ To add extensions, use `additionalExtensions`:
214
225
  }
215
226
  ```
216
227
 
217
- To completely replace supported extensions, use `extensions`:
228
+ To replace extensions, use `extensions`:
218
229
 
219
230
  ```ts
220
231
  {
221
- extensions: 'png jpg' // serve png and jpg files only
232
+ extensions: 'png jpg' // serve png and jpg files only
222
233
  }
223
234
  ```
224
235
 
225
236
  ### Image attributes
226
237
 
227
- The module automatically adds `width` and `height` attributes to images.
238
+ You can add image size hints to the generated images.
228
239
 
229
- Opt out of this by passing `false`:
240
+ To add `style` aspect-ratio:
230
241
 
231
242
  ```ts
232
243
  {
233
- imageAttrs: false
244
+ imageSize: 'style'
234
245
  }
235
246
  ```
236
247
 
248
+ To add `width` and `height` attributes:
249
+
250
+ ```ts
251
+ {
252
+ imageSize: 'attrs'
253
+ }
254
+ ```
255
+
256
+ You can even add both if you need to.
257
+
237
258
  ## Development
238
259
 
239
260
  Should you wish to develop the project, the scripts are:
package/dist/module.d.ts CHANGED
@@ -4,7 +4,7 @@ interface ModuleOptions {
4
4
  output?: string;
5
5
  additionalExtensions?: string;
6
6
  extensions?: string;
7
- imageAttrs?: boolean;
7
+ imageSize?: string;
8
8
  debug?: boolean;
9
9
  }
10
10
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
package/dist/module.json CHANGED
@@ -1,5 +1,8 @@
1
1
  {
2
- "name": "content-assets",
2
+ "name": "nuxt-content-assets",
3
3
  "configKey": "content-assets",
4
- "version": "0.5.1"
4
+ "compatibility": {
5
+ "nuxt": "^3.0.0"
6
+ },
7
+ "version": "0.5.3"
5
8
  }
package/dist/module.mjs CHANGED
@@ -6,8 +6,37 @@ 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
+ }, {});
20
+ }
21
+
22
+ const defaults = {
23
+ assetsDir: "assets/content",
24
+ assetsPattern: "[folder]/[file]"
25
+ };
26
+ const imageExtensions = matchWords("png jpg jpeg gif svg webp ico");
27
+ const mediaExtensions = matchWords("mp3 m4a wav mp4 mov webm ogg avi flv avchd");
28
+ const fileExtensions = matchWords("pdf doc docx xls xlsx ppt pptx odp key");
29
+ const extensions = [
30
+ ...imageExtensions,
31
+ ...mediaExtensions,
32
+ ...fileExtensions
33
+ ];
34
+
35
+ const moduleName = "nuxt-content-assets";
36
+ const moduleKey = "content-assets";
37
+
9
38
  function log(...data) {
10
- console.info(`[${name}]`, ...data);
39
+ console.info(`[${moduleKey}]`, ...data);
11
40
  }
12
41
 
13
42
  function matchWords(value) {
@@ -39,48 +68,25 @@ const replacers = {
39
68
  hash: (src) => hash({ src })
40
69
  };
41
70
 
42
- const name = "content-assets";
43
- const defaults = {
44
- assetsDir: "assets/content",
45
- assetsPattern: "[name]-[hash].[ext]"
46
- };
47
- const imageExtensions = matchWords("png jpg jpeg gif svg webp ico");
48
- const mediaExtensions = matchWords("mp3 m4a wav mp4 mov webm ogg avi flv avchd");
49
- const fileExtensions = matchWords("pdf doc docx xls xlsx ppt pptx odp key");
50
- const extensions = [
51
- ...imageExtensions,
52
- ...mediaExtensions,
53
- ...fileExtensions
54
- ];
55
-
56
- function getSources(sources) {
57
- return Object.keys(sources).reduce((output, key) => {
58
- const source = sources[key];
59
- if (source) {
60
- const { driver, base } = source;
61
- if (driver === "fs") {
62
- output[key] = base;
63
- }
64
- }
65
- return output;
66
- }, {});
67
- }
68
-
69
71
  const resolve = createResolver(import.meta.url).resolve;
70
72
  const module = defineNuxtModule({
71
73
  meta: {
72
- name
74
+ name: moduleName,
75
+ configKey: moduleKey,
76
+ compatibility: {
77
+ nuxt: "^3.0.0"
78
+ }
73
79
  },
74
80
  defaults: {
75
81
  output: `${defaults.assetsDir}/${defaults.assetsPattern}`,
76
82
  extensions: "",
77
83
  additionalExtensions: "",
78
- imageAttrs: true,
84
+ imageSize: "",
79
85
  debug: false
80
86
  },
81
87
  setup(options, nuxt) {
82
88
  var _a;
83
- const pluginPath = resolve("./runtime/server") + "/plugins/plugin";
89
+ const pluginPath = resolve("./runtime/plugin");
84
90
  const buildPath = nuxt.options.buildDir;
85
91
  const cachePath = Path.resolve(buildPath, "content-assets");
86
92
  if (options.debug) {
@@ -116,16 +122,22 @@ const module = defineNuxtModule({
116
122
  function getAssetConfig(pattern, src, dir) {
117
123
  let width = void 0;
118
124
  let height = void 0;
119
- if (options.imageAttrs && isImage(src)) {
125
+ let ratio = "";
126
+ if (options.imageSize && isImage(src)) {
120
127
  const size = getImageSize(src);
121
- width = size.width;
122
- height = size.height;
128
+ if (options.imageSize.includes("style")) {
129
+ ratio = `${size.width}/${size.height}`;
130
+ }
131
+ if (options.imageSize.includes("attrs")) {
132
+ width = size.width;
133
+ height = size.height;
134
+ }
123
135
  }
124
136
  const id = Path.join(Path.basename(dir), Path.relative(dir, src)).replaceAll("/", ":");
125
137
  const file = interpolatePattern(pattern, src, dir);
126
138
  const trg = Path.join(cachePath, assetsDir, file);
127
139
  const rel = Path.join("/", assetsDir, file);
128
- return { id, file, trg, rel, width, height };
140
+ return { id, file, trg, rel, width, height, ratio };
129
141
  }
130
142
  const publicFolder = Path.join(cachePath, assetsDir);
131
143
  const sourceFolders = Object.values(sources);
@@ -159,15 +171,15 @@ ${paths.join("\n")}
159
171
  `export const assets = ${JSON.stringify(assets)}`,
160
172
  `export const sources = ${JSON.stringify(sources)}`
161
173
  ].join("\n");
162
- nuxt.options.alias[`#${name}`] = addTemplate({
163
- filename: `${name}.mjs`,
174
+ nuxt.options.alias[`#${moduleName}`] = addTemplate({
175
+ filename: `${moduleName}.mjs`,
164
176
  getContents: () => virtualConfig
165
177
  }).dst;
166
178
  nuxt.hook("nitro:config", async (config) => {
167
179
  config.plugins || (config.plugins = []);
168
180
  config.plugins.push(pluginPath);
169
181
  config.virtual || (config.virtual = {});
170
- config.virtual[`#${name}`] = virtualConfig;
182
+ config.virtual[`#${moduleName}`] = virtualConfig;
171
183
  config.publicAssets || (config.publicAssets = []);
172
184
  config.publicAssets.push({
173
185
  dir: cachePath
@@ -0,0 +1,9 @@
1
+ export declare const defaults: {
2
+ assetsDir: string;
3
+ assetsPattern: string;
4
+ };
5
+ export declare const imageExtensions: RegExpMatchArray | [];
6
+ export declare const mediaExtensions: RegExpMatchArray | [];
7
+ export declare const fileExtensions: RegExpMatchArray | [];
8
+ export declare const extensions: string[];
9
+ export declare const tags: string[];
@@ -0,0 +1,14 @@
1
+ import { matchWords } from "./utils/assets.mjs";
2
+ export const defaults = {
3
+ assetsDir: "assets/content",
4
+ assetsPattern: "[folder]/[file]"
5
+ };
6
+ export const imageExtensions = matchWords("png jpg jpeg gif svg webp ico");
7
+ export const mediaExtensions = matchWords("mp3 m4a wav mp4 mov webm ogg avi flv avchd");
8
+ export const fileExtensions = matchWords("pdf doc docx xls xlsx ppt pptx odp key");
9
+ export const extensions = [
10
+ ...imageExtensions,
11
+ ...mediaExtensions,
12
+ ...fileExtensions
13
+ ];
14
+ export const tags = ["img", "video", "audio", "source", "embed", "iframe", "a"];
@@ -1,8 +1,8 @@
1
1
  import Path from "path";
2
2
  import { visit } from "unist-util-visit";
3
- import { assets, sources } from "#content-assets";
4
- import { tags } from "../../../config";
5
- import { isValidAsset, walk } from "../../../utils";
3
+ import { isValidAsset, walk } from "./utils/index.mjs";
4
+ import { tags } from "./options.mjs";
5
+ import { assets, sources } from "#nuxt-content-assets";
6
6
  function getDocPath(id) {
7
7
  const parts = id.split(":");
8
8
  const key = parts.shift();
@@ -29,7 +29,7 @@ export default defineNitroPlugin(async (nitroApp) => {
29
29
  }, filter);
30
30
  visit(file.body, (n) => tags.includes(n.tag), (node) => {
31
31
  if (node.props.src) {
32
- const { rel, width, height } = getAsset(absDoc, node.props.src);
32
+ const { rel, width, height, ratio } = getAsset(absDoc, node.props.src);
33
33
  if (rel) {
34
34
  node.props.src = rel;
35
35
  }
@@ -37,6 +37,9 @@ export default defineNitroPlugin(async (nitroApp) => {
37
37
  node.props.width = width;
38
38
  node.props.height = height;
39
39
  }
40
+ if (ratio) {
41
+ node.props.style = `aspect-ratio:${ratio}`;
42
+ }
40
43
  } else if (node.tag === "a") {
41
44
  if (node.props.href) {
42
45
  const { rel } = getAsset(absDoc, node.props.href);
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Get matched words from a string
3
+ */
4
+ export declare function matchWords(value: string): RegExpMatchArray | [];
5
+ /**
6
+ * Test path to be relative
7
+ */
8
+ export declare function isRelative(path: string): boolean;
9
+ /**
10
+ * Test path for image extension
11
+ */
12
+ export declare function isImage(path: string): boolean;
13
+ /**
14
+ * Test path for asset extension
15
+ */
16
+ export declare function isAsset(path: string): boolean;
17
+ /**
18
+ * Test if value is a valid asset
19
+ */
20
+ 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>;
@@ -0,0 +1,42 @@
1
+ import Path from "path";
2
+ import { hash as ohash } from "ohash";
3
+ 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
+ export function isRelative(path) {
9
+ return !(path.startsWith("http") || Path.isAbsolute(path));
10
+ }
11
+ export function isImage(path) {
12
+ const ext = Path.extname(path).substring(1);
13
+ return imageExtensions.includes(ext);
14
+ }
15
+ export function isAsset(path) {
16
+ const ext = Path.extname(path).substring(1);
17
+ return extensions.includes(ext);
18
+ }
19
+ export function isValidAsset(value) {
20
+ return typeof value === "string" && isAsset(value) && isRelative(value);
21
+ }
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,7 @@
1
+ import type { MountOptions } from '@nuxt/content';
2
+ /**
3
+ * Get content source folders
4
+ *
5
+ * @param sources
6
+ */
7
+ export declare function getSources(sources: Record<string, MountOptions>): Record<string, string>;
@@ -0,0 +1,12 @@
1
+ export function getSources(sources) {
2
+ return Object.keys(sources).reduce((output, key) => {
3
+ const source = sources[key];
4
+ if (source) {
5
+ const { driver, base } = source;
6
+ if (driver === "fs") {
7
+ output[key] = base;
8
+ }
9
+ }
10
+ return output;
11
+ }, {});
12
+ }
@@ -0,0 +1,2 @@
1
+ export declare function log(...data: any[]): void;
2
+ export declare function warn(...data: any[]): void;
@@ -0,0 +1,7 @@
1
+ import { moduleKey } from "../../config";
2
+ export function log(...data) {
3
+ console.info(`[${moduleKey}]`, ...data);
4
+ }
5
+ export function warn(...data) {
6
+ console.warn(`[${moduleKey}]`, ...data);
7
+ }
@@ -0,0 +1,4 @@
1
+ export * from './content';
2
+ export * from './object';
3
+ export * from './assets';
4
+ export * from './debug';
@@ -0,0 +1,4 @@
1
+ export * from "./content.mjs";
2
+ export * from "./object.mjs";
3
+ export * from "./assets.mjs";
4
+ export * from "./debug.mjs";
@@ -0,0 +1,10 @@
1
+ export type Callback = (value: any, parent?: any, key?: string | number) => void;
2
+ export type Filter = (value: any, key?: string | number) => boolean | void;
3
+ /**
4
+ * Walk an object structure
5
+ *
6
+ * @param node
7
+ * @param callback
8
+ * @param filter
9
+ */
10
+ export declare function walk(node: any, callback: Callback, filter?: Filter): void;
@@ -0,0 +1,22 @@
1
+ export function walk(node, callback, filter) {
2
+ function visit(node2, callback2, parent, key) {
3
+ if (filter) {
4
+ const result = filter(node2, key);
5
+ if (result === false) {
6
+ return;
7
+ }
8
+ }
9
+ if (Array.isArray(node2)) {
10
+ node2.forEach((value, index) => {
11
+ visit(value, callback2, node2, index);
12
+ });
13
+ } else if (typeof node2 === "object" && node2 !== null) {
14
+ Object.keys(node2).forEach((key2) => {
15
+ visit(node2[key2], callback2, node2, key2);
16
+ });
17
+ } else {
18
+ callback2(node2, parent, key);
19
+ }
20
+ }
21
+ visit(node, callback);
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-content-assets",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Enable locally-located assets in Nuxt Content",
5
5
  "repository": "davestewart/nuxt-content-assets",
6
6
  "license": "MIT",