nuxt-content-assets 1.1.1 → 1.2.1
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 -0
- package/dist/module.d.ts +21 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +349 -168
- package/dist/runtime/assets/public.d.ts +41 -0
- package/dist/runtime/assets/public.mjs +111 -0
- package/dist/runtime/{services/sources.d.ts → assets/source.d.ts} +4 -4
- package/dist/runtime/{services/sources.mjs → assets/source.mjs} +4 -14
- package/dist/runtime/content/parsed.d.ts +5 -0
- package/dist/runtime/content/parsed.mjs +32 -0
- package/dist/runtime/content/plugin.mjs +65 -0
- package/dist/runtime/sockets/factory.d.ts +5 -1
- package/dist/runtime/sockets/factory.mjs +3 -10
- package/dist/runtime/sockets/plugin.d.ts +3 -0
- package/dist/runtime/sockets/plugin.mjs +31 -8
- package/dist/runtime/sockets/setup.d.ts +1 -1
- package/dist/runtime/sockets/setup.mjs +15 -2
- package/dist/runtime/utils/config.d.ts +11 -0
- package/dist/runtime/utils/config.mjs +12 -0
- package/dist/runtime/utils/content.d.ts +16 -0
- package/dist/runtime/utils/content.mjs +43 -0
- package/dist/runtime/utils/index.d.ts +2 -0
- package/dist/runtime/utils/index.mjs +2 -0
- package/dist/runtime/utils/object.d.ts +7 -3
- package/dist/runtime/utils/object.mjs +5 -2
- package/dist/runtime/utils/path.mjs +1 -1
- package/dist/types.d.ts +2 -6
- package/package.json +2 -4
- package/dist/runtime/config.d.ts +0 -2
- package/dist/runtime/config.mjs +0 -2
- package/dist/runtime/options.d.ts +0 -10
- package/dist/runtime/options.mjs +0 -18
- package/dist/runtime/plugin.mjs +0 -121
- package/dist/runtime/services/assets.d.ts +0 -25
- package/dist/runtime/services/assets.mjs +0 -23
- package/dist/runtime/services/index.d.ts +0 -3
- package/dist/runtime/services/index.mjs +0 -3
- package/dist/runtime/services/paths.d.ts +0 -8
- package/dist/runtime/services/paths.mjs +0 -26
- package/dist/runtime/sockets/composable.d.ts +0 -2
- package/dist/runtime/sockets/composable.mjs +0 -12
- /package/dist/runtime/{plugin.d.ts → content/plugin.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -283,6 +283,8 @@ If they do, then the attribute or property is rewritten with the absolute path.
|
|
|
283
283
|
|
|
284
284
|
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.
|
|
285
285
|
|
|
286
|
+
In development, file watching propagates asset changes to the public folder, updates related cached content, and notifies the browser via web sockets to refresh any loaded images.
|
|
287
|
+
|
|
286
288
|
## Development
|
|
287
289
|
|
|
288
290
|
Should you wish to develop the project, the scripts are:
|
package/dist/module.d.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
3
|
interface ModuleOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Image size hints
|
|
6
|
+
*
|
|
7
|
+
* @example 'attrs style url'
|
|
8
|
+
* @default 'style'
|
|
9
|
+
*/
|
|
4
10
|
imageSize?: string | string[] | false;
|
|
5
|
-
|
|
11
|
+
/**
|
|
12
|
+
* List of content extensions; anything else as an asset
|
|
13
|
+
*
|
|
14
|
+
* @example 'md'
|
|
15
|
+
* @default 'md csv ya?ml json'
|
|
16
|
+
*/
|
|
17
|
+
contentExtensions?: string | string[];
|
|
18
|
+
/**
|
|
19
|
+
* Display debug messages
|
|
20
|
+
*
|
|
21
|
+
* @example true
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
6
24
|
debug?: boolean;
|
|
7
25
|
}
|
|
26
|
+
|
|
8
27
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
9
28
|
|
|
10
|
-
export {
|
|
29
|
+
export { _default as default };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,14 +2,15 @@ import * as Fs from 'fs';
|
|
|
2
2
|
import * as Path from 'path';
|
|
3
3
|
import Path__default from 'path';
|
|
4
4
|
import { useNuxt, createResolver, defineNuxtModule, addPlugin } from '@nuxt/kit';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
5
|
+
import { visit, SKIP, CONTINUE } from 'unist-util-visit';
|
|
6
|
+
import { listen } from 'listhen';
|
|
7
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
8
8
|
import githubDriver from 'unstorage/drivers/github';
|
|
9
9
|
import fsDriver from 'unstorage/drivers/fs';
|
|
10
|
+
import { createStorage } from 'unstorage';
|
|
11
|
+
import getImageSize from 'image-size';
|
|
12
|
+
import debounce from 'debounce';
|
|
10
13
|
import 'ohash';
|
|
11
|
-
import { listen } from 'listhen';
|
|
12
|
-
import { WebSocketServer, WebSocket } from 'ws';
|
|
13
14
|
|
|
14
15
|
function matchTokens(value) {
|
|
15
16
|
let tokens = [];
|
|
@@ -33,22 +34,16 @@ function deKey(path) {
|
|
|
33
34
|
return path.replace(/^[^:]+:/, "");
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const defaults = {
|
|
37
|
-
// inject image size into the rendered html
|
|
38
|
-
imageSize: "style",
|
|
39
|
-
// treat these extensions as content
|
|
40
|
-
contentExtensions: "md csv ya?ml json",
|
|
41
|
-
// output debug messages
|
|
42
|
-
debug: false
|
|
43
|
-
};
|
|
44
37
|
const extensions = {
|
|
45
38
|
// used to get image size
|
|
46
39
|
image: matchTokens("png jpg jpeg gif svg webp ico"),
|
|
47
40
|
// unused for now
|
|
48
41
|
media: matchTokens("mp3 m4a wav mp4 mov webm ogg avi flv avchd")
|
|
49
42
|
};
|
|
50
|
-
function
|
|
51
|
-
|
|
43
|
+
function makeIgnores(extensions2) {
|
|
44
|
+
const matched = matchTokens(extensions2);
|
|
45
|
+
const ignored = matched.join("|");
|
|
46
|
+
return `[^:]+\\.(?!(${ignored})$)`;
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
function removeQuery(path) {
|
|
@@ -68,6 +63,73 @@ function isAsset(path) {
|
|
|
68
63
|
return !isArticle(path);
|
|
69
64
|
}
|
|
70
65
|
|
|
66
|
+
function walk(node, callback, filter) {
|
|
67
|
+
function visit(node2, callback2, parent, key) {
|
|
68
|
+
if (filter) {
|
|
69
|
+
const result = filter(node2, key);
|
|
70
|
+
if (result === false) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(node2)) {
|
|
75
|
+
node2.forEach((value, index) => {
|
|
76
|
+
visit(value, callback2, node2, index);
|
|
77
|
+
});
|
|
78
|
+
} else if (isObject(node2)) {
|
|
79
|
+
Object.keys(node2).forEach((key2) => {
|
|
80
|
+
visit(node2[key2], callback2, node2, key2);
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
callback2(node2, parent, key);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
visit(node, callback, { node }, "node");
|
|
87
|
+
}
|
|
88
|
+
function isObject(data) {
|
|
89
|
+
return data && typeof data === "object" && !Array.isArray(data);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function walkMeta(content, callback) {
|
|
93
|
+
walk(content, callback, (value, key) => !(String(key).startsWith("_") || key === "body"));
|
|
94
|
+
}
|
|
95
|
+
function walkBody(content, callback) {
|
|
96
|
+
visit(content.body, (node) => node.type === "element", (node) => {
|
|
97
|
+
const { tag, props } = node;
|
|
98
|
+
const excluded = tags.exclude.includes(tag);
|
|
99
|
+
if (excluded) {
|
|
100
|
+
return SKIP;
|
|
101
|
+
}
|
|
102
|
+
const included = tags.include.includes(tag);
|
|
103
|
+
if (included || !props) {
|
|
104
|
+
return CONTINUE;
|
|
105
|
+
}
|
|
106
|
+
callback(node);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const tags = {
|
|
110
|
+
// unlikely to contain assets
|
|
111
|
+
exclude: matchTokens({
|
|
112
|
+
container: "pre code code-inline",
|
|
113
|
+
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",
|
|
114
|
+
headers: "h1 h2 h3 h4 h5 h6",
|
|
115
|
+
controls: "input textarea button select optgroup option label legend datalist output",
|
|
116
|
+
media: "map area canvas svg",
|
|
117
|
+
other: "style script noscript template",
|
|
118
|
+
empty: "hr br"
|
|
119
|
+
}),
|
|
120
|
+
// may contain assets
|
|
121
|
+
include: matchTokens({
|
|
122
|
+
content: "main header footer section article aside details dialog summary data object nav blockquote div span p",
|
|
123
|
+
table: "table caption th tr td thead tbody tfoot col colgroup",
|
|
124
|
+
media: "figcaption figure picture",
|
|
125
|
+
form: "form fieldset",
|
|
126
|
+
list: "ul ol li dir dl dt dd",
|
|
127
|
+
formatting: "strong b em i"
|
|
128
|
+
}),
|
|
129
|
+
// assets
|
|
130
|
+
assets: "a img audio source track video embed"
|
|
131
|
+
};
|
|
132
|
+
|
|
71
133
|
const label = "[content-assets]";
|
|
72
134
|
function log(...data) {
|
|
73
135
|
console.info(label, ...data);
|
|
@@ -82,6 +144,20 @@ ${items.map((item) => ` - ${item}`).join("\n")}
|
|
|
82
144
|
`);
|
|
83
145
|
}
|
|
84
146
|
|
|
147
|
+
function buildQuery(...expr) {
|
|
148
|
+
const output = expr.map((expr2) => expr2.replace(/^[?&]+|&+$/g, "")).filter((s) => s);
|
|
149
|
+
if (output.length) {
|
|
150
|
+
const [first, ...rest] = output;
|
|
151
|
+
const isParam = (expr2) => /^[^?]+=[^=]+$/.test(expr2);
|
|
152
|
+
return !isParam(first) ? rest.length > 0 ? first + (first.includes("?") ? "&" : "?") + rest.join("&") : first : "?" + output.join("&");
|
|
153
|
+
}
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readFile(path, asJson = false) {
|
|
158
|
+
const text = Fs.readFileSync(path, { encoding: "utf8" });
|
|
159
|
+
return asJson ? JSON.parse(text) : text;
|
|
160
|
+
}
|
|
85
161
|
function writeFile(path, data) {
|
|
86
162
|
const text = typeof data === "object" ? JSON.stringify(data, null, " ") : String(data);
|
|
87
163
|
createFolder(Path.dirname(path));
|
|
@@ -109,32 +185,117 @@ function removeFolder(path) {
|
|
|
109
185
|
}
|
|
110
186
|
}
|
|
111
187
|
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
188
|
+
function createWebSocket() {
|
|
189
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
190
|
+
const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
|
|
191
|
+
const broadcast = (data) => {
|
|
192
|
+
data = JSON.stringify(data);
|
|
193
|
+
for (const client of wss.clients) {
|
|
194
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
195
|
+
client.send(data);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const handlers = [];
|
|
200
|
+
const addHandler = (callback) => {
|
|
201
|
+
handlers.push(callback);
|
|
202
|
+
};
|
|
203
|
+
wss.on("connection", (socket) => {
|
|
204
|
+
socket.addEventListener("message", (event) => {
|
|
205
|
+
let data;
|
|
206
|
+
try {
|
|
207
|
+
data = JSON.parse(event.data || "{}");
|
|
208
|
+
} catch (err) {
|
|
209
|
+
}
|
|
210
|
+
if (data) {
|
|
211
|
+
handlers.forEach((callback) => callback(data));
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
116
215
|
return {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
216
|
+
wss,
|
|
217
|
+
serve,
|
|
218
|
+
broadcast,
|
|
219
|
+
addHandler,
|
|
220
|
+
close: () => {
|
|
221
|
+
wss.clients.forEach((client) => client.close());
|
|
222
|
+
return new Promise((resolve) => wss.close(resolve));
|
|
223
|
+
}
|
|
120
224
|
};
|
|
121
225
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
226
|
+
|
|
227
|
+
function makeChannelBroker(ws2) {
|
|
228
|
+
const handlers = [];
|
|
229
|
+
const broadcast = (channel, data) => {
|
|
230
|
+
ws2.broadcast({ channel, data });
|
|
231
|
+
};
|
|
232
|
+
const addHandler = (channel, callback) => {
|
|
233
|
+
handlers.push({ channel, callback });
|
|
234
|
+
};
|
|
235
|
+
ws2.addHandler(function(message) {
|
|
236
|
+
if (isObject(message)) {
|
|
237
|
+
const { channel } = message;
|
|
238
|
+
handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(message));
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
broadcast,
|
|
243
|
+
addHandler
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const ws = createWebSocket();
|
|
247
|
+
const broker = makeChannelBroker(ws);
|
|
248
|
+
async function setupSocketServer(channel, handler) {
|
|
249
|
+
const nuxt = useNuxt();
|
|
250
|
+
nuxt.hook("nitro:init", async (nitro) => {
|
|
251
|
+
if (!nuxt._socketServer) {
|
|
252
|
+
const defaults = nuxt.options.runtimeConfig.content.watch.ws;
|
|
253
|
+
const port = defaults.port.port;
|
|
254
|
+
const { server, url } = await listen(() => "Nuxt Content Assets", {
|
|
255
|
+
hostname: defaults.hostname,
|
|
256
|
+
port: {
|
|
257
|
+
port: port + 1,
|
|
258
|
+
portRange: [
|
|
259
|
+
port + 1,
|
|
260
|
+
port + 40
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
showURL: false
|
|
264
|
+
});
|
|
265
|
+
nuxt._socketServer = server;
|
|
266
|
+
server.on("upgrade", ws.serve);
|
|
267
|
+
const wsUrl = url.replace("http", "ws");
|
|
268
|
+
log(`Websocket listening on "${wsUrl}"`);
|
|
269
|
+
nitro.options.runtimeConfig.public.sockets = {
|
|
270
|
+
wsUrl
|
|
271
|
+
};
|
|
272
|
+
nitro.hooks.hook("close", async () => {
|
|
273
|
+
await ws.close();
|
|
274
|
+
await server.close();
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
const instance = {
|
|
279
|
+
send(data) {
|
|
280
|
+
broker.broadcast(channel, data);
|
|
281
|
+
return this;
|
|
282
|
+
},
|
|
283
|
+
addHandler(callback) {
|
|
284
|
+
broker.addHandler(channel, callback);
|
|
285
|
+
return this;
|
|
128
286
|
}
|
|
287
|
+
};
|
|
288
|
+
if (handler) {
|
|
289
|
+
instance.addHandler(handler);
|
|
129
290
|
}
|
|
130
|
-
return
|
|
291
|
+
return instance;
|
|
131
292
|
}
|
|
132
293
|
|
|
133
294
|
function isAssetId(id) {
|
|
134
295
|
const path = toPath(id);
|
|
135
296
|
return !isExcluded(path) && isAsset(path);
|
|
136
297
|
}
|
|
137
|
-
function
|
|
298
|
+
function makeSourceStorage(source, key = "") {
|
|
138
299
|
const storage = createStorage();
|
|
139
300
|
const options = typeof source === "string" ? { driver: "fs", base: source } : source;
|
|
140
301
|
switch (options.driver) {
|
|
@@ -216,7 +377,7 @@ function makeSourceManager(key, source, publicPath, callback) {
|
|
|
216
377
|
}
|
|
217
378
|
return paths;
|
|
218
379
|
}
|
|
219
|
-
const storage =
|
|
380
|
+
const storage = makeSourceStorage(source, key);
|
|
220
381
|
storage.watch(onWatch);
|
|
221
382
|
return {
|
|
222
383
|
storage,
|
|
@@ -225,141 +386,158 @@ function makeSourceManager(key, source, publicPath, callback) {
|
|
|
225
386
|
};
|
|
226
387
|
}
|
|
227
388
|
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const broadcast = (data) => {
|
|
235
|
-
data = JSON.stringify(data);
|
|
236
|
-
for (const client of wss.clients) {
|
|
237
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
238
|
-
client.send(data);
|
|
239
|
-
}
|
|
389
|
+
function makeAssetsManager(publicPath) {
|
|
390
|
+
const indexKey = "assets.json";
|
|
391
|
+
const storage = makeSourceStorage(Path.join(publicPath, ".."));
|
|
392
|
+
storage.watch(async (event, key) => {
|
|
393
|
+
if (event === "update" && key === indexKey) {
|
|
394
|
+
await load();
|
|
240
395
|
}
|
|
241
|
-
};
|
|
242
|
-
const handlers = [];
|
|
243
|
-
const addHandler = (callback) => {
|
|
244
|
-
handlers.push(callback);
|
|
245
|
-
};
|
|
246
|
-
wss.on("connection", (socket) => {
|
|
247
|
-
socket.addEventListener("message", (event) => {
|
|
248
|
-
let data;
|
|
249
|
-
try {
|
|
250
|
-
data = JSON.parse(event.data || "{}");
|
|
251
|
-
} catch (err) {
|
|
252
|
-
}
|
|
253
|
-
if (data) {
|
|
254
|
-
handlers.forEach((callback) => callback(data));
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
396
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
397
|
+
const assets = {};
|
|
398
|
+
async function load() {
|
|
399
|
+
const data = await storage.getItem(indexKey);
|
|
400
|
+
Object.assign(assets, data || {});
|
|
401
|
+
}
|
|
402
|
+
const save = debounce(function() {
|
|
403
|
+
storage.setItem(indexKey, assets);
|
|
404
|
+
}, 50);
|
|
405
|
+
function resolveAsset(content, relAsset, registerContent = false) {
|
|
406
|
+
const srcDir = Path.dirname(content._file);
|
|
407
|
+
const srcAsset = Path.join(srcDir, relAsset);
|
|
408
|
+
const asset = assets[srcAsset];
|
|
409
|
+
if (asset && registerContent) {
|
|
410
|
+
const { _id } = content;
|
|
411
|
+
if (!asset.content.includes(_id)) {
|
|
412
|
+
asset.content.push(_id);
|
|
413
|
+
save();
|
|
414
|
+
}
|
|
266
415
|
}
|
|
416
|
+
return asset || {};
|
|
417
|
+
}
|
|
418
|
+
function setAsset(path) {
|
|
419
|
+
const { srcRel, srcAttr } = getAssetPaths(publicPath, path);
|
|
420
|
+
const { width, height } = getAssetSize(path);
|
|
421
|
+
const oldAsset = assets[srcRel];
|
|
422
|
+
const newAsset = {
|
|
423
|
+
srcAttr,
|
|
424
|
+
content: oldAsset?.content || [],
|
|
425
|
+
width,
|
|
426
|
+
height
|
|
427
|
+
};
|
|
428
|
+
assets[srcRel] = newAsset;
|
|
429
|
+
save();
|
|
430
|
+
return newAsset;
|
|
431
|
+
}
|
|
432
|
+
function getAsset(path) {
|
|
433
|
+
const { srcRel } = getAssetPaths(publicPath, path);
|
|
434
|
+
return srcRel ? { ...assets[srcRel] } : void 0;
|
|
435
|
+
}
|
|
436
|
+
function removeAsset(path) {
|
|
437
|
+
const { srcRel } = getAssetPaths(publicPath, path);
|
|
438
|
+
const asset = assets[srcRel];
|
|
439
|
+
if (asset) {
|
|
440
|
+
delete assets[srcRel];
|
|
441
|
+
save();
|
|
442
|
+
}
|
|
443
|
+
return asset;
|
|
444
|
+
}
|
|
445
|
+
void load();
|
|
446
|
+
return {
|
|
447
|
+
setAsset,
|
|
448
|
+
getAsset,
|
|
449
|
+
removeAsset,
|
|
450
|
+
resolveAsset
|
|
267
451
|
};
|
|
268
452
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
function makeChannelBroker(ws2) {
|
|
274
|
-
const handlers = [];
|
|
275
|
-
const broadcast = (channel, data) => {
|
|
276
|
-
ws2.broadcast({ channel, data });
|
|
277
|
-
};
|
|
278
|
-
const addHandler = (channel, callback) => {
|
|
279
|
-
handlers.push({ channel, callback });
|
|
280
|
-
};
|
|
281
|
-
ws2.addHandler(function(message) {
|
|
282
|
-
if (isObject(message)) {
|
|
283
|
-
const { channel } = message;
|
|
284
|
-
handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(message));
|
|
285
|
-
}
|
|
286
|
-
});
|
|
453
|
+
function getAssetPaths(srcDir, srcAbs) {
|
|
454
|
+
const srcRel = Path.relative(srcDir, srcAbs);
|
|
455
|
+
const srcAttr = "/" + srcRel;
|
|
287
456
|
return {
|
|
288
|
-
|
|
289
|
-
|
|
457
|
+
srcRel,
|
|
458
|
+
srcAttr
|
|
290
459
|
};
|
|
291
460
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
wsUrl
|
|
310
|
-
};
|
|
311
|
-
nitro.hooks.hook("close", async () => {
|
|
312
|
-
await ws.close();
|
|
313
|
-
await server.close();
|
|
314
|
-
});
|
|
461
|
+
function getAssetSize(srcAbs) {
|
|
462
|
+
if (isImage(srcAbs)) {
|
|
463
|
+
try {
|
|
464
|
+
return getImageSize(srcAbs);
|
|
465
|
+
} catch (err) {
|
|
466
|
+
warn(`could not read image "${srcAbs}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return {};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function rewriteContent(path, asset) {
|
|
473
|
+
const { parsed } = readFile(path, true);
|
|
474
|
+
const { srcAttr, width, height } = asset;
|
|
475
|
+
walkMeta(parsed, (value, parent, key) => {
|
|
476
|
+
if (value.startsWith(srcAttr)) {
|
|
477
|
+
parent[key] = parent[key].replace(/width=\d+&height=\d+/, `width=${width}&height=${height}`);
|
|
315
478
|
}
|
|
316
479
|
});
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
480
|
+
walkBody(parsed, function(node) {
|
|
481
|
+
const { tag, props } = node;
|
|
482
|
+
if (tag === "img" && props?.src?.startsWith(srcAttr)) {
|
|
483
|
+
props.src = buildQuery(srcAttr, `time=${Date.now()}`);
|
|
484
|
+
if (props.width) {
|
|
485
|
+
props.width = width;
|
|
486
|
+
}
|
|
487
|
+
if (props.height) {
|
|
488
|
+
props.height = height;
|
|
489
|
+
}
|
|
490
|
+
if (props.style) {
|
|
491
|
+
const ratio = `${width}/${height}`;
|
|
492
|
+
if (typeof props.style === "string") {
|
|
493
|
+
props.style = props.style.replace(/aspect-ratio: \d+\/\d+/, `aspect-ratio: ${ratio}`);
|
|
494
|
+
} else if (props.style.aspectRatio) {
|
|
495
|
+
props.style.aspectRatio = ratio;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
325
498
|
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
return instance;
|
|
499
|
+
});
|
|
500
|
+
writeFile(path, { module: true, parsed });
|
|
501
|
+
return parsed;
|
|
331
502
|
}
|
|
332
503
|
|
|
333
504
|
const resolve = createResolver(import.meta.url).resolve;
|
|
505
|
+
const meta = {
|
|
506
|
+
name: "nuxt-content-assets",
|
|
507
|
+
configKey: "contentAssets",
|
|
508
|
+
compatibility: {
|
|
509
|
+
nuxt: "^3.0.0"
|
|
510
|
+
}
|
|
511
|
+
};
|
|
334
512
|
const module = defineNuxtModule({
|
|
335
|
-
meta
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
513
|
+
meta,
|
|
514
|
+
defaults: {
|
|
515
|
+
imageSize: "style",
|
|
516
|
+
contentExtensions: "md csv ya?ml json",
|
|
517
|
+
debug: false
|
|
341
518
|
},
|
|
342
|
-
defaults,
|
|
343
519
|
async setup(options, nuxt) {
|
|
344
520
|
var _a, _b;
|
|
345
|
-
const pluginPath = resolve("./runtime/plugin");
|
|
346
521
|
const buildPath = nuxt.options.buildDir;
|
|
347
|
-
const
|
|
348
|
-
const publicPath = Path.join(
|
|
349
|
-
const
|
|
522
|
+
const assetsPath = Path.join(buildPath, "content-assets");
|
|
523
|
+
const publicPath = Path.join(assetsPath, "public");
|
|
524
|
+
const contentPath = Path.join(buildPath, "content-cache/parsed");
|
|
350
525
|
if (options.debug) {
|
|
351
526
|
log("Removing cache folders...");
|
|
352
527
|
}
|
|
353
528
|
removeFolder(Path.join(buildPath, "content-cache"));
|
|
354
|
-
removeFolder(
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
(
|
|
529
|
+
removeFolder(assetsPath);
|
|
530
|
+
const { contentExtensions } = options;
|
|
531
|
+
if (contentExtensions) {
|
|
532
|
+
(_a = nuxt.options).content || (_a.content = {});
|
|
533
|
+
if (nuxt.options.content) {
|
|
534
|
+
(_b = nuxt.options.content).ignores || (_b.ignores = []);
|
|
535
|
+
}
|
|
536
|
+
const ignores = makeIgnores(contentExtensions);
|
|
537
|
+
nuxt.options.content?.ignores.push(ignores);
|
|
358
538
|
}
|
|
359
|
-
const ignores = getIgnores(options.contentExtensions);
|
|
360
|
-
nuxt.options.content?.ignores.push(ignores);
|
|
361
539
|
const imageFlags = matchTokens(options.imageSize);
|
|
362
|
-
const sources = nuxt.options._layers.map((layer) => layer.config?.content?.sources).reduce((output, sources2) => {
|
|
540
|
+
const sources = Array.from(nuxt.options._layers).map((layer) => layer.config?.content?.sources).reduce((output, sources2) => {
|
|
363
541
|
if (sources2) {
|
|
364
542
|
Object.assign(output, sources2);
|
|
365
543
|
}
|
|
@@ -374,31 +552,33 @@ const module = defineNuxtModule({
|
|
|
374
552
|
};
|
|
375
553
|
}
|
|
376
554
|
}
|
|
377
|
-
|
|
378
|
-
const { srcRel, srcAttr } = getAssetPaths(publicPath, src);
|
|
379
|
-
const { width, height } = getAssetSizes(src);
|
|
380
|
-
assets[srcRel] = {
|
|
381
|
-
srcAttr,
|
|
382
|
-
width,
|
|
383
|
-
height
|
|
384
|
-
};
|
|
385
|
-
saveAssets();
|
|
386
|
-
return srcAttr;
|
|
387
|
-
}
|
|
388
|
-
function removeAsset(src) {
|
|
389
|
-
const { srcRel, srcAttr } = getAssetPaths(publicPath, src);
|
|
390
|
-
delete assets[srcRel];
|
|
391
|
-
saveAssets();
|
|
392
|
-
return srcAttr;
|
|
393
|
-
}
|
|
394
|
-
const saveAssets = debounce(() => {
|
|
395
|
-
writeFile(indexPath, assets);
|
|
396
|
-
}, 50);
|
|
397
|
-
const assets = {};
|
|
555
|
+
const assets = makeAssetsManager(publicPath);
|
|
398
556
|
function onAssetChange(event, absTrg) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
557
|
+
let src = "";
|
|
558
|
+
let width;
|
|
559
|
+
let height;
|
|
560
|
+
if (event === "update") {
|
|
561
|
+
const oldAsset = isImage(absTrg) && imageFlags.length ? assets.getAsset(absTrg) : null;
|
|
562
|
+
const newAsset = assets.setAsset(absTrg);
|
|
563
|
+
width = newAsset.width;
|
|
564
|
+
height = newAsset.height;
|
|
565
|
+
if (oldAsset) {
|
|
566
|
+
if (oldAsset.width !== newAsset.width || oldAsset.height !== newAsset.height) {
|
|
567
|
+
newAsset.content.forEach(async (id) => {
|
|
568
|
+
const path = Path.join(contentPath, toPath(id));
|
|
569
|
+
rewriteContent(path, newAsset);
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
src = newAsset.srcAttr;
|
|
574
|
+
} else {
|
|
575
|
+
const asset = assets.removeAsset(absTrg);
|
|
576
|
+
if (asset) {
|
|
577
|
+
src = asset.srcAttr;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (src && socket) {
|
|
581
|
+
socket.send({ event, src, width, height });
|
|
402
582
|
}
|
|
403
583
|
}
|
|
404
584
|
addPlugin(resolve("./runtime/sockets/plugin"));
|
|
@@ -413,15 +593,16 @@ const module = defineNuxtModule({
|
|
|
413
593
|
nuxt.hook("build:before", async function() {
|
|
414
594
|
for (const [key, manager] of Object.entries(managers)) {
|
|
415
595
|
const paths = await manager.init();
|
|
416
|
-
paths.forEach((path) =>
|
|
596
|
+
paths.forEach((path) => assets.setAsset(path));
|
|
417
597
|
if (options.debug) {
|
|
418
598
|
list(`Copied "${key}" assets`, paths.map((path) => Path.relative(publicPath, path)));
|
|
419
599
|
}
|
|
420
600
|
}
|
|
421
601
|
});
|
|
602
|
+
const pluginPath = resolve("./runtime/content/plugin");
|
|
422
603
|
const makeVar = (name, value) => `export const ${name} = ${JSON.stringify(value)};`;
|
|
423
604
|
const virtualConfig = [
|
|
424
|
-
makeVar("
|
|
605
|
+
makeVar("publicPath", publicPath),
|
|
425
606
|
makeVar("imageFlags", imageFlags),
|
|
426
607
|
makeVar("debug", options.debug)
|
|
427
608
|
].join("\n");
|
|
@@ -429,7 +610,7 @@ const module = defineNuxtModule({
|
|
|
429
610
|
config.plugins || (config.plugins = []);
|
|
430
611
|
config.plugins.push(pluginPath);
|
|
431
612
|
config.virtual || (config.virtual = {});
|
|
432
|
-
config.virtual[`#${
|
|
613
|
+
config.virtual[`#${meta.name}`] = () => {
|
|
433
614
|
return virtualConfig;
|
|
434
615
|
};
|
|
435
616
|
config.publicAssets || (config.publicAssets = []);
|