nuxt-content-assets 0.10.1 → 0.10.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 +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +29 -13
- package/dist/runtime/plugin.mjs +68 -20
- package/dist/runtime/services/sources.mjs +17 -3
- package/dist/runtime/utils/assert.mjs +1 -1
- package/dist/runtime/utils/debug.mjs +3 -3
- package/dist/runtime/utils/string.d.ts +1 -1
- package/dist/runtime/utils/string.mjs +13 -4
- package/package.json +1 -1
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
|
|
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
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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(":", "/");
|
|
@@ -53,17 +62,15 @@ function isArticle(path) {
|
|
|
53
62
|
return path.endsWith(".md");
|
|
54
63
|
}
|
|
55
64
|
function isAsset(path) {
|
|
56
|
-
return !
|
|
65
|
+
return !isArticle(path);
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
const
|
|
60
|
-
const moduleKey = "contentAssets";
|
|
61
|
-
|
|
68
|
+
const label = "[content-assets]";
|
|
62
69
|
function log(...data) {
|
|
63
|
-
console.info(
|
|
70
|
+
console.info(label, ...data);
|
|
64
71
|
}
|
|
65
72
|
function warn(...data) {
|
|
66
|
-
console.warn(
|
|
73
|
+
console.warn(label, ...data);
|
|
67
74
|
}
|
|
68
75
|
function list(message, items) {
|
|
69
76
|
log(`${message}:
|
|
@@ -139,6 +146,10 @@ function getAssetSizes(srcAbs, hints) {
|
|
|
139
146
|
};
|
|
140
147
|
}
|
|
141
148
|
|
|
149
|
+
function isAssetId(id) {
|
|
150
|
+
const path = toPath(id);
|
|
151
|
+
return !isExcluded(path) && isAsset(path);
|
|
152
|
+
}
|
|
142
153
|
function makeStorage(source, key = "") {
|
|
143
154
|
const storage = createStorage();
|
|
144
155
|
const options = typeof source === "string" ? { driver: "fs", base: source } : source;
|
|
@@ -164,7 +175,7 @@ function makeStorage(source, key = "") {
|
|
|
164
175
|
}
|
|
165
176
|
function makeSourceManager(key, source, publicPath, callback) {
|
|
166
177
|
async function onWatch(event, key2) {
|
|
167
|
-
if (
|
|
178
|
+
if (isAssetId(key2)) {
|
|
168
179
|
const path = event === "update" ? await copyItem(key2) : removeItem(key2);
|
|
169
180
|
if (callback) {
|
|
170
181
|
callback(event, path);
|
|
@@ -210,7 +221,7 @@ function makeSourceManager(key, source, publicPath, callback) {
|
|
|
210
221
|
}
|
|
211
222
|
async function getKeys() {
|
|
212
223
|
const keys = await storage.getKeys();
|
|
213
|
-
return keys.
|
|
224
|
+
return keys.filter(isAssetId);
|
|
214
225
|
}
|
|
215
226
|
async function init() {
|
|
216
227
|
const keys = await getKeys();
|
|
@@ -230,6 +241,9 @@ function makeSourceManager(key, source, publicPath, callback) {
|
|
|
230
241
|
};
|
|
231
242
|
}
|
|
232
243
|
|
|
244
|
+
const moduleName = "nuxt-content-assets";
|
|
245
|
+
const moduleKey = "contentAssets";
|
|
246
|
+
|
|
233
247
|
function createWebSocket() {
|
|
234
248
|
const wss = new WebSocketServer({ noServer: true });
|
|
235
249
|
const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
|
|
@@ -422,8 +436,10 @@ const module = defineNuxtModule({
|
|
|
422
436
|
}
|
|
423
437
|
}
|
|
424
438
|
});
|
|
439
|
+
const makeVar = (name, value) => `export const ${name} = ${JSON.stringify(value)};`;
|
|
425
440
|
const virtualConfig = [
|
|
426
|
-
|
|
441
|
+
makeVar("cachePath", cachePath),
|
|
442
|
+
makeVar("debug", options.debug)
|
|
427
443
|
].join("\n");
|
|
428
444
|
nuxt.hook("nitro:config", async (config) => {
|
|
429
445
|
config.plugins || (config.plugins = []);
|
package/dist/runtime/plugin.mjs
CHANGED
|
@@ -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,44 +14,92 @@ storage.watch(async (event, key) => {
|
|
|
14
14
|
});
|
|
15
15
|
let assets = {};
|
|
16
16
|
void updateAssets();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const tags = {
|
|
18
|
+
// unlikely to contain assets
|
|
19
|
+
exclude: 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
|
+
include: 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
|
-
|
|
24
|
-
|
|
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(
|
|
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
|
-
|
|
62
|
+
const { tag, props } = node;
|
|
63
|
+
const excluded = tags.exclude.includes(tag);
|
|
64
|
+
if (excluded) {
|
|
65
|
+
return SKIP;
|
|
66
|
+
}
|
|
67
|
+
const included = tags.include.includes(tag);
|
|
68
|
+
if (included || !props) {
|
|
69
|
+
return CONTINUE;
|
|
70
|
+
}
|
|
71
|
+
for (const [prop, value] of Object.entries(props)) {
|
|
36
72
|
if (typeof value !== "string") {
|
|
37
73
|
return;
|
|
38
74
|
}
|
|
39
|
-
const { srcAttr, width, height, ratio } = getAsset(
|
|
75
|
+
const { srcAttr, width, height, ratio } = getAsset(value);
|
|
40
76
|
if (srcAttr) {
|
|
41
77
|
node.props[prop] = srcAttr;
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
if (node.tag === "img") {
|
|
79
|
+
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;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
48
91
|
}
|
|
49
|
-
if (node.tag === "a"
|
|
50
|
-
node.props.target
|
|
92
|
+
if (node.tag === "a") {
|
|
93
|
+
node.props.target ||= "_blank";
|
|
51
94
|
}
|
|
95
|
+
updated.push(`page: ${tag}[${prop}] to "${srcAttr}"`);
|
|
52
96
|
}
|
|
53
97
|
}
|
|
54
98
|
});
|
|
99
|
+
if (debug && updated.length) {
|
|
100
|
+
list(`Processed "/${srcDoc}"`, updated);
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
55
103
|
}
|
|
56
104
|
});
|
|
57
105
|
};
|
|
@@ -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 {
|
|
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;
|
|
@@ -28,7 +42,7 @@ export function makeStorage(source, key = "") {
|
|
|
28
42
|
}
|
|
29
43
|
export function makeSourceManager(key, source, publicPath, callback) {
|
|
30
44
|
async function onWatch(event, key2) {
|
|
31
|
-
if (
|
|
45
|
+
if (isAssetId(key2)) {
|
|
32
46
|
const path = event === "update" ? await copyItem(key2) : removeItem(key2);
|
|
33
47
|
if (callback) {
|
|
34
48
|
callback(event, path);
|
|
@@ -74,7 +88,7 @@ export function makeSourceManager(key, source, publicPath, callback) {
|
|
|
74
88
|
}
|
|
75
89
|
async function getKeys() {
|
|
76
90
|
const keys = await storage.getKeys();
|
|
77
|
-
return keys.
|
|
91
|
+
return keys.filter(isAssetId);
|
|
78
92
|
}
|
|
79
93
|
async function init() {
|
|
80
94
|
const keys = await getKeys();
|
|
@@ -14,7 +14,7 @@ export function isArticle(path) {
|
|
|
14
14
|
return path.endsWith(".md");
|
|
15
15
|
}
|
|
16
16
|
export function isAsset(path) {
|
|
17
|
-
return !
|
|
17
|
+
return !isArticle(path);
|
|
18
18
|
}
|
|
19
19
|
export function isValidAsset(value) {
|
|
20
20
|
return typeof value === "string" && isAsset(value) && isRelative(value);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
const label = "[content-assets]";
|
|
2
2
|
export function log(...data) {
|
|
3
|
-
console.info(
|
|
3
|
+
console.info(label, ...data);
|
|
4
4
|
}
|
|
5
5
|
export function warn(...data) {
|
|
6
|
-
console.warn(
|
|
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
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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(":", "/");
|