nuxt-content-assets 0.10.0 → 0.10.2

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
@@ -237,9 +237,9 @@ If you want to see what the module does as it runs, set `debug` to true:
237
237
 
238
238
  When Nuxt builds, the module scans all content sources for assets, copies them to an accessible public assets folder, and indexes path and image metadata.
239
239
 
240
- After Nuxt Content has run the parsed content is traversed, and both element attributes and frontmatter properties are checked to see if they resolve to the indexed asset paths.
240
+ After Nuxt Content has run, the parsed content is traversed, and both element attributes and frontmatter properties are checked to see if they resolve to the indexed asset paths.
241
241
 
242
- If they do, then the attribute or property is rewritten with the absolute path. If the asset is an image, then the element or path is optionally updated with size attributes or size query string.
242
+ If they do, then the attribute or property is rewritten with the absolute path. If the asset is an image, then the element or metadata is optionally updated with size attributes or a query string.
243
243
 
244
244
  Finally, Nitro serves the site, and any requests made to the transformed asset paths should be picked up and the *copied* asset served by the browser.
245
245
 
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.10.0"
7
+ "version": "0.10.2"
8
8
  }
package/dist/module.mjs CHANGED
@@ -12,10 +12,19 @@ import { listen } from 'listhen';
12
12
  import { WebSocketServer, WebSocket } from 'ws';
13
13
 
14
14
  function matchTokens(value) {
15
- const tokens = typeof value === "string" ? value.match(/[^\s,|]+/g) || [] : Array.isArray(value) ? value.filter((value2) => typeof value2 === "string").reduce((output, input) => {
16
- return [...output, ...matchTokens(input)];
17
- }, []) : [];
18
- return Array.from(new Set(tokens));
15
+ let tokens = [];
16
+ if (typeof value === "string") {
17
+ tokens = value.match(/[^\s,|]+/g) || [];
18
+ } else if (Array.isArray(value)) {
19
+ tokens = value.filter((value2) => typeof value2 === "string").reduce((output, input) => {
20
+ return [...output, ...matchTokens(input)];
21
+ }, []);
22
+ } else if (!!value && typeof value === "object") {
23
+ tokens = Object.values(value).reduce((output, value2) => {
24
+ return [...output, ...matchTokens(value2)];
25
+ }, []);
26
+ }
27
+ return tokens.length ? Array.from(new Set(tokens)) : tokens;
19
28
  }
20
29
  function toPath(key) {
21
30
  return key.replaceAll(":", "/");
@@ -28,7 +37,7 @@ const defaults = {
28
37
  // inject image size into the rendered html
29
38
  imageSize: "attrs",
30
39
  // treat these extensions as content
31
- contentExtensions: "mdx? csv ya?ml json",
40
+ contentExtensions: "md csv ya?ml json",
32
41
  // output debug messages
33
42
  debug: false
34
43
  };
@@ -42,26 +51,26 @@ function getIgnores(extensions2) {
42
51
  return `^((?!(${matchTokens(extensions2).join("|")})).)*$`;
43
52
  }
44
53
 
54
+ function isExcluded(path) {
55
+ return path.split("/").some((segment) => segment.startsWith(".") || segment.startsWith("_"));
56
+ }
45
57
  function isImage(path) {
46
58
  const ext = Path__default.extname(path).substring(1);
47
59
  return extensions.image.includes(ext);
48
60
  }
49
61
  function isArticle(path) {
50
- return /\.mdx?$/.test(path);
62
+ return path.endsWith(".md");
51
63
  }
52
64
  function isAsset(path) {
53
- const ext = Path__default.extname(path);
54
- return !!ext && ext !== ".DS_Store" && !isArticle(path);
65
+ return !isArticle(path);
55
66
  }
56
67
 
57
- const moduleName = "nuxt-content-assets";
58
- const moduleKey = "contentAssets";
59
-
68
+ const label = "[content-assets]";
60
69
  function log(...data) {
61
- console.info(`[${moduleKey}]`, ...data);
70
+ console.info(label, ...data);
62
71
  }
63
72
  function warn(...data) {
64
- console.warn(`[${moduleKey}]`, ...data);
73
+ console.warn(label, ...data);
65
74
  }
66
75
  function list(message, items) {
67
76
  log(`${message}:
@@ -137,6 +146,10 @@ function getAssetSizes(srcAbs, hints) {
137
146
  };
138
147
  }
139
148
 
149
+ function isAssetId(id) {
150
+ const path = toPath(id);
151
+ return !isExcluded(path) && isAsset(path);
152
+ }
140
153
  function makeStorage(source, key = "") {
141
154
  const storage = createStorage();
142
155
  const options = typeof source === "string" ? { driver: "fs", base: source } : source;
@@ -144,7 +157,10 @@ function makeStorage(source, key = "") {
144
157
  case "fs":
145
158
  storage.mount(key, fsDriver({
146
159
  ...options,
147
- ignore: ["[^:]+?\\.md"]
160
+ ignore: [
161
+ "[^:]+?\\.md",
162
+ "_dir\\.yml"
163
+ ]
148
164
  }));
149
165
  break;
150
166
  case "github":
@@ -159,7 +175,7 @@ function makeStorage(source, key = "") {
159
175
  }
160
176
  function makeSourceManager(key, source, publicPath, callback) {
161
177
  async function onWatch(event, key2) {
162
- if (isAsset(key2)) {
178
+ if (isAssetId(key2)) {
163
179
  const path = event === "update" ? await copyItem(key2) : removeItem(key2);
164
180
  if (callback) {
165
181
  callback(event, path);
@@ -205,7 +221,7 @@ function makeSourceManager(key, source, publicPath, callback) {
205
221
  }
206
222
  async function getKeys() {
207
223
  const keys = await storage.getKeys();
208
- return keys.filter(isAsset);
224
+ return keys.filter(isAssetId);
209
225
  }
210
226
  async function init() {
211
227
  const keys = await getKeys();
@@ -225,6 +241,9 @@ function makeSourceManager(key, source, publicPath, callback) {
225
241
  };
226
242
  }
227
243
 
244
+ const moduleName = "nuxt-content-assets";
245
+ const moduleKey = "contentAssets";
246
+
228
247
  function createWebSocket() {
229
248
  const wss = new WebSocketServer({ noServer: true });
230
249
  const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
@@ -417,8 +436,10 @@ const module = defineNuxtModule({
417
436
  }
418
437
  }
419
438
  });
439
+ const makeVar = (name, value) => `export const ${name} = ${JSON.stringify(value)};`;
420
440
  const virtualConfig = [
421
- `export const cachePath = '${cachePath}'`
441
+ makeVar("cachePath", cachePath),
442
+ makeVar("debug", options.debug)
422
443
  ].join("\n");
423
444
  nuxt.hook("nitro:config", async (config) => {
424
445
  config.plugins || (config.plugins = []);
@@ -3,7 +3,7 @@ export const defaults = {
3
3
  // inject image size into the rendered html
4
4
  imageSize: "attrs",
5
5
  // treat these extensions as content
6
- contentExtensions: "mdx? csv ya?ml json",
6
+ contentExtensions: "md csv ya?ml json",
7
7
  // output debug messages
8
8
  debug: false
9
9
  };
@@ -1,7 +1,7 @@
1
1
  import Path from "path";
2
- import { visit } from "unist-util-visit";
3
- import { deKey, isValidAsset, toPath, walk } from "./utils/index.mjs";
4
- import { cachePath } from "#nuxt-content-assets";
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";
5
5
  import { makeStorage } from "./services/index.mjs";
6
6
  async function updateAssets() {
7
7
  assets = await storage.getItem("assets.json");
@@ -14,30 +14,70 @@ storage.watch(async (event, key) => {
14
14
  });
15
15
  let assets = {};
16
16
  void updateAssets();
17
- function getAsset(srcDoc, relAsset) {
18
- const srcAsset = Path.join(Path.dirname(srcDoc), relAsset);
19
- return assets[srcAsset] || {};
20
- }
17
+ const tags = {
18
+ // unlikely to contain assets
19
+ content: matchTokens({
20
+ container: "pre code code-inline",
21
+ formatting: "acronym abbr address bdi bdo big center cite del dfn font ins kbd mark meter progress q rp rt ruby s samp small strike sub sup time tt u var wbr",
22
+ headers: "h1 h2 h3 h4 h5 h6",
23
+ controls: "input textarea button select optgroup option label legend datalist output",
24
+ media: "map area canvas svg",
25
+ other: "style script noscript template",
26
+ empty: "hr br"
27
+ }),
28
+ // may contain assets
29
+ props: matchTokens({
30
+ content: "main header footer section article aside details dialog summary data object nav blockquote div span p",
31
+ table: "table caption th tr td thead tbody tfoot col colgroup",
32
+ media: "figcaption figure picture",
33
+ form: "form fieldset",
34
+ list: "ul ol li dir dl dt dd",
35
+ formatting: "strong b em i"
36
+ }),
37
+ // assets
38
+ assets: "a img audio source track video embed"
39
+ };
21
40
  const plugin = async (nitro) => {
22
41
  nitro.hooks.hook("content:file:afterParse", async (file) => {
23
- if (file._id.endsWith(".md")) {
24
- const srcDoc = toPath(deKey(file._id));
42
+ const { _id } = file;
43
+ if (_id.endsWith(".md")) {
44
+ const srcDoc = toPath(deKey(_id));
45
+ const srcDir = Path.dirname(srcDoc);
46
+ const updated = [];
47
+ const getAsset = (relAsset) => {
48
+ const srcAsset = Path.join(srcDir, relAsset);
49
+ return assets[srcAsset] || {};
50
+ };
25
51
  const filter = (value, key) => !(String(key).startsWith("_") || key === "body");
26
52
  walk(file, (value, parent, key) => {
27
53
  if (isValidAsset(value)) {
28
- const { srcAttr, query } = getAsset(srcDoc, value);
54
+ const { srcAttr, query } = getAsset(value);
29
55
  if (srcAttr) {
30
56
  parent[key] = srcAttr + (query || "");
57
+ updated.push(`meta: ${key} to "${srcAttr}"`);
31
58
  }
32
59
  }
33
60
  }, filter);
34
61
  visit(file.body, (node) => node.type === "element", (node) => {
35
- for (const [prop, value] of Object.entries(node.props)) {
62
+ const { tag, props } = node;
63
+ const noContent = tags.content.includes(tag);
64
+ if (noContent) {
65
+ return SKIP;
66
+ }
67
+ const noProps = tags.props.includes(tag);
68
+ if (noProps) {
69
+ return CONTINUE;
70
+ }
71
+ if (!props) {
72
+ return CONTINUE;
73
+ }
74
+ for (const [prop, value] of Object.entries(props)) {
36
75
  if (typeof value !== "string") {
37
76
  return;
38
77
  }
39
- const { srcAttr, width, height, ratio } = getAsset(srcDoc, value);
78
+ const { srcAttr, width, height, ratio } = getAsset(value);
40
79
  if (srcAttr) {
80
+ updated.push(`page: ${tag}[${prop}] to "${srcAttr}"`);
41
81
  node.props[prop] = srcAttr;
42
82
  if (width && height) {
43
83
  node.props.width = width;
@@ -52,6 +92,10 @@ const plugin = async (nitro) => {
52
92
  }
53
93
  }
54
94
  });
95
+ if (debug && updated.length) {
96
+ list(`Processed "/${srcDoc}"`, updated);
97
+ console.log();
98
+ }
55
99
  }
56
100
  });
57
101
  };
@@ -2,7 +2,21 @@ import * as Path from "path";
2
2
  import { createStorage } from "unstorage";
3
3
  import githubDriver from "unstorage/drivers/github";
4
4
  import fsDriver from "unstorage/drivers/fs";
5
- import { warn, isAsset, toPath, removeFile, copyFile, writeBlob, writeFile, deKey } from "../utils/index.mjs";
5
+ import {
6
+ warn,
7
+ isAsset,
8
+ toPath,
9
+ removeFile,
10
+ copyFile,
11
+ writeBlob,
12
+ writeFile,
13
+ deKey,
14
+ isExcluded
15
+ } from "../utils/index.mjs";
16
+ function isAssetId(id) {
17
+ const path = toPath(id);
18
+ return !isExcluded(path) && isAsset(path);
19
+ }
6
20
  export function makeStorage(source, key = "") {
7
21
  const storage = createStorage();
8
22
  const options = typeof source === "string" ? { driver: "fs", base: source } : source;
@@ -10,7 +24,10 @@ export function makeStorage(source, key = "") {
10
24
  case "fs":
11
25
  storage.mount(key, fsDriver({
12
26
  ...options,
13
- ignore: ["[^:]+?\\.md"]
27
+ ignore: [
28
+ "[^:]+?\\.md",
29
+ "_dir\\.yml"
30
+ ]
14
31
  }));
15
32
  break;
16
33
  case "github":
@@ -25,7 +42,7 @@ export function makeStorage(source, key = "") {
25
42
  }
26
43
  export function makeSourceManager(key, source, publicPath, callback) {
27
44
  async function onWatch(event, key2) {
28
- if (isAsset(key2)) {
45
+ if (isAssetId(key2)) {
29
46
  const path = event === "update" ? await copyItem(key2) : removeItem(key2);
30
47
  if (callback) {
31
48
  callback(event, path);
@@ -71,7 +88,7 @@ export function makeSourceManager(key, source, publicPath, callback) {
71
88
  }
72
89
  async function getKeys() {
73
90
  const keys = await storage.getKeys();
74
- return keys.filter(isAsset);
91
+ return keys.filter(isAssetId);
75
92
  }
76
93
  async function init() {
77
94
  const keys = await getKeys();
@@ -3,15 +3,20 @@
3
3
  */
4
4
  export declare function isRelative(path: string): boolean;
5
5
  /**
6
- * Test path or id for image extension
6
+ * Test if path is excluded (_partial or .ignored)
7
+ * @param path
8
+ */
9
+ export declare function isExcluded(path: string): boolean;
10
+ /**
11
+ * Test path for image extension
7
12
  */
8
13
  export declare function isImage(path: string): boolean;
9
14
  /**
10
- * Test path or id is markdown
15
+ * Test path is markdown
11
16
  */
12
17
  export declare function isArticle(path: string): boolean;
13
18
  /**
14
- * Test path or id is asset
19
+ * Test path is asset
15
20
  */
16
21
  export declare function isAsset(path: string): boolean;
17
22
  /**
@@ -3,16 +3,18 @@ import { extensions } from "../options.mjs";
3
3
  export function isRelative(path) {
4
4
  return !(path.startsWith("http") || Path.isAbsolute(path));
5
5
  }
6
+ export function isExcluded(path) {
7
+ return path.split("/").some((segment) => segment.startsWith(".") || segment.startsWith("_"));
8
+ }
6
9
  export function isImage(path) {
7
10
  const ext = Path.extname(path).substring(1);
8
11
  return extensions.image.includes(ext);
9
12
  }
10
13
  export function isArticle(path) {
11
- return /\.mdx?$/.test(path);
14
+ return path.endsWith(".md");
12
15
  }
13
16
  export function isAsset(path) {
14
- const ext = Path.extname(path);
15
- return !!ext && ext !== ".DS_Store" && !isArticle(path);
17
+ return !isArticle(path);
16
18
  }
17
19
  export function isValidAsset(value) {
18
20
  return typeof value === "string" && isAsset(value) && isRelative(value);
@@ -1,9 +1,9 @@
1
- import { moduleKey } from "../config.mjs";
1
+ const label = "[content-assets]";
2
2
  export function log(...data) {
3
- console.info(`[${moduleKey}]`, ...data);
3
+ console.info(label, ...data);
4
4
  }
5
5
  export function warn(...data) {
6
- console.warn(`[${moduleKey}]`, ...data);
6
+ console.warn(label, ...data);
7
7
  }
8
8
  export function list(message, items) {
9
9
  log(`${message}:
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Tokens may be separated by space, comma or pipe
5
5
  */
6
- export declare function matchTokens(value?: string | unknown[]): string[];
6
+ export declare function matchTokens(value: any): string[];
7
7
  export declare function toPath(key: string): string;
8
8
  export declare function toKey(path: string): any;
9
9
  export declare function deKey(path: string): string;
@@ -1,8 +1,17 @@
1
1
  export function matchTokens(value) {
2
- const tokens = typeof value === "string" ? value.match(/[^\s,|]+/g) || [] : Array.isArray(value) ? value.filter((value2) => typeof value2 === "string").reduce((output, input) => {
3
- return [...output, ...matchTokens(input)];
4
- }, []) : [];
5
- return Array.from(new Set(tokens));
2
+ let tokens = [];
3
+ if (typeof value === "string") {
4
+ tokens = value.match(/[^\s,|]+/g) || [];
5
+ } else if (Array.isArray(value)) {
6
+ tokens = value.filter((value2) => typeof value2 === "string").reduce((output, input) => {
7
+ return [...output, ...matchTokens(input)];
8
+ }, []);
9
+ } else if (!!value && typeof value === "object") {
10
+ tokens = Object.values(value).reduce((output, value2) => {
11
+ return [...output, ...matchTokens(value2)];
12
+ }, []);
13
+ }
14
+ return tokens.length ? Array.from(new Set(tokens)) : tokens;
6
15
  }
7
16
  export function toPath(key) {
8
17
  return key.replaceAll(":", "/");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-content-assets",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "Enable locally-located assets in Nuxt Content",
5
5
  "repository": "davestewart/nuxt-content-assets",
6
6
  "license": "MIT",