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 +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +38 -17
- package/dist/runtime/options.mjs +1 -1
- package/dist/runtime/plugin.mjs +56 -12
- package/dist/runtime/services/sources.mjs +21 -4
- package/dist/runtime/utils/assert.d.ts +8 -3
- package/dist/runtime/utils/assert.mjs +5 -3
- 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(":", "/");
|
|
@@ -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: "
|
|
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
|
|
62
|
+
return path.endsWith(".md");
|
|
51
63
|
}
|
|
52
64
|
function isAsset(path) {
|
|
53
|
-
|
|
54
|
-
return !!ext && ext !== ".DS_Store" && !isArticle(path);
|
|
65
|
+
return !isArticle(path);
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
const
|
|
58
|
-
const moduleKey = "contentAssets";
|
|
59
|
-
|
|
68
|
+
const label = "[content-assets]";
|
|
60
69
|
function log(...data) {
|
|
61
|
-
console.info(
|
|
70
|
+
console.info(label, ...data);
|
|
62
71
|
}
|
|
63
72
|
function warn(...data) {
|
|
64
|
-
console.warn(
|
|
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: [
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
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 = []);
|
package/dist/runtime/options.mjs
CHANGED
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,30 +14,70 @@ 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
|
+
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
|
-
|
|
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 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(
|
|
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 {
|
|
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: [
|
|
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 (
|
|
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(
|
|
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
|
|
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
|
|
15
|
+
* Test path is markdown
|
|
11
16
|
*/
|
|
12
17
|
export declare function isArticle(path: string): boolean;
|
|
13
18
|
/**
|
|
14
|
-
* Test path
|
|
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
|
|
14
|
+
return path.endsWith(".md");
|
|
12
15
|
}
|
|
13
16
|
export function isAsset(path) {
|
|
14
|
-
|
|
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
|
-
|
|
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(":", "/");
|