nuxt-content-assets 1.1.0 → 1.2.0

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.
Files changed (42) hide show
  1. package/README.md +25 -19
  2. package/dist/module.d.ts +19 -1
  3. package/dist/module.json +4 -3
  4. package/dist/module.mjs +350 -189
  5. package/dist/runtime/assets/public.d.ts +41 -0
  6. package/dist/runtime/assets/public.mjs +111 -0
  7. package/dist/runtime/{services/sources.d.ts → assets/source.d.ts} +4 -4
  8. package/dist/runtime/{services/sources.mjs → assets/source.mjs} +4 -14
  9. package/dist/runtime/content/parsed.d.ts +5 -0
  10. package/dist/runtime/content/parsed.mjs +32 -0
  11. package/dist/runtime/content/plugin.mjs +65 -0
  12. package/dist/runtime/sockets/factory.d.ts +5 -1
  13. package/dist/runtime/sockets/factory.mjs +3 -10
  14. package/dist/runtime/sockets/plugin.d.ts +3 -0
  15. package/dist/runtime/sockets/plugin.mjs +31 -8
  16. package/dist/runtime/sockets/setup.d.ts +1 -1
  17. package/dist/runtime/sockets/setup.mjs +15 -2
  18. package/dist/runtime/utils/config.d.ts +11 -0
  19. package/dist/runtime/utils/config.mjs +12 -0
  20. package/dist/runtime/utils/content.d.ts +16 -0
  21. package/dist/runtime/utils/content.mjs +43 -0
  22. package/dist/runtime/utils/index.d.ts +2 -0
  23. package/dist/runtime/utils/index.mjs +2 -0
  24. package/dist/runtime/utils/object.d.ts +7 -3
  25. package/dist/runtime/utils/object.mjs +5 -2
  26. package/dist/runtime/utils/path.mjs +1 -1
  27. package/dist/types.d.ts +1 -5
  28. package/package.json +2 -4
  29. package/dist/runtime/config.d.ts +0 -2
  30. package/dist/runtime/config.mjs +0 -2
  31. package/dist/runtime/options.d.ts +0 -10
  32. package/dist/runtime/options.mjs +0 -18
  33. package/dist/runtime/plugin.mjs +0 -118
  34. package/dist/runtime/services/assets.d.ts +0 -32
  35. package/dist/runtime/services/assets.mjs +0 -42
  36. package/dist/runtime/services/index.d.ts +0 -3
  37. package/dist/runtime/services/index.mjs +0 -3
  38. package/dist/runtime/services/paths.d.ts +0 -8
  39. package/dist/runtime/services/paths.mjs +0 -26
  40. package/dist/runtime/sockets/composable.d.ts +0 -2
  41. package/dist/runtime/sockets/composable.mjs +0 -12
  42. /package/dist/runtime/{plugin.d.ts → content/plugin.d.ts} +0 -0
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 debounce from 'debounce';
6
- import getImageSize from 'image-size';
7
- import { createStorage } from 'unstorage';
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 getIgnores(extensions2) {
51
- return `^((?!(${matchTokens(extensions2).join("|")})).)*$`;
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,51 +185,117 @@ function removeFolder(path) {
109
185
  }
110
186
  }
111
187
 
112
- function getAssetPaths(srcDir, srcAbs) {
113
- const srcRel = Path.relative(srcDir, srcAbs);
114
- const srcAttr = "/" + srcRel;
115
- const id = srcRel.replaceAll("/", ":");
116
- return {
117
- id,
118
- srcRel,
119
- srcAttr
120
- };
121
- }
122
- function getAssetSizes(srcAbs, hints) {
123
- let width = void 0;
124
- let height = void 0;
125
- let ratio = void 0;
126
- let query = void 0;
127
- if (hints.length && isImage(srcAbs)) {
128
- try {
129
- const size = getImageSize(srcAbs);
130
- if (hints.includes("attrs")) {
131
- width = size.width;
132
- height = size.height;
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);
133
196
  }
134
- if (hints.includes("style")) {
135
- ratio = `${size.width}/${size.height}`;
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) {
136
209
  }
137
- if (hints.includes("src") || hints.includes("url")) {
138
- query = `width=${size.width}&height=${size.height}`;
210
+ if (data) {
211
+ handlers.forEach((callback) => callback(data));
139
212
  }
140
- } catch (err) {
141
- warn(`could not read image "${srcAbs}`);
213
+ });
214
+ });
215
+ return {
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));
142
223
  }
143
- }
224
+ };
225
+ }
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
+ });
144
241
  return {
145
- width,
146
- height,
147
- ratio,
148
- query
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;
286
+ }
149
287
  };
288
+ if (handler) {
289
+ instance.addHandler(handler);
290
+ }
291
+ return instance;
150
292
  }
151
293
 
152
294
  function isAssetId(id) {
153
295
  const path = toPath(id);
154
296
  return !isExcluded(path) && isAsset(path);
155
297
  }
156
- function makeStorage(source, key = "") {
298
+ function makeSourceStorage(source, key = "") {
157
299
  const storage = createStorage();
158
300
  const options = typeof source === "string" ? { driver: "fs", base: source } : source;
159
301
  switch (options.driver) {
@@ -235,7 +377,7 @@ function makeSourceManager(key, source, publicPath, callback) {
235
377
  }
236
378
  return paths;
237
379
  }
238
- const storage = makeStorage(source, key);
380
+ const storage = makeSourceStorage(source, key);
239
381
  storage.watch(onWatch);
240
382
  return {
241
383
  storage,
@@ -244,141 +386,159 @@ function makeSourceManager(key, source, publicPath, callback) {
244
386
  };
245
387
  }
246
388
 
247
- const moduleName = "nuxt-content-assets";
248
- const moduleKey = "contentAssets";
249
-
250
- function createWebSocket() {
251
- const wss = new WebSocketServer({ noServer: true });
252
- const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
253
- const broadcast = (data) => {
254
- data = JSON.stringify(data);
255
- for (const client of wss.clients) {
256
- if (client.readyState === WebSocket.OPEN) {
257
- client.send(data);
258
- }
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();
259
395
  }
260
- };
261
- const handlers = [];
262
- const addHandler = (callback) => {
263
- handlers.push(callback);
264
- };
265
- wss.on("connection", (socket) => {
266
- socket.addEventListener("message", (event) => {
267
- let data;
268
- try {
269
- data = JSON.parse(event.data || "{}");
270
- } catch (err) {
271
- }
272
- if (data) {
273
- handlers.forEach((callback) => callback(data));
274
- }
275
- });
276
396
  });
277
- return {
278
- wss,
279
- serve,
280
- broadcast,
281
- addHandler,
282
- close: () => {
283
- wss.clients.forEach((client) => client.close());
284
- return new Promise((resolve) => wss.close(resolve));
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
+ }
285
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
286
451
  };
287
452
  }
288
-
289
- function isObject(data) {
290
- return data && typeof data === "object" && !Array.isArray(data);
291
- }
292
- function makeChannelBroker(ws2) {
293
- const handlers = [];
294
- const broadcast = (channel, data) => {
295
- ws2.broadcast({ channel, data });
296
- };
297
- const addHandler = (channel, callback) => {
298
- handlers.push({ channel, callback });
299
- };
300
- ws2.addHandler(function(message) {
301
- if (isObject(message)) {
302
- const { channel } = message;
303
- handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(message));
304
- }
305
- });
453
+ function getAssetPaths(srcDir, srcAbs) {
454
+ const srcRel = Path.relative(srcDir, srcAbs);
455
+ const srcAttr = "/" + srcRel;
306
456
  return {
307
- broadcast,
308
- addHandler
457
+ srcRel,
458
+ srcAttr
309
459
  };
310
460
  }
311
- const ws = createWebSocket();
312
- const broker = makeChannelBroker(ws);
313
- async function setupSocketServer(channel, handler) {
314
- const nuxt = useNuxt();
315
- nuxt.hook("nitro:init", async (nitro) => {
316
- if (!nuxt._socketServer) {
317
- const defaults = nuxt.options.runtimeConfig.content.watch.ws;
318
- const { server, url } = await listen(() => "Nuxt Content Assets", {
319
- port: defaults.port.port + 1,
320
- hostname: defaults.hostname,
321
- showURL: false
322
- });
323
- nuxt._socketServer = server;
324
- server.on("upgrade", ws.serve);
325
- const wsUrl = url.replace("http", "ws");
326
- log(`Websocket listening on "${wsUrl}"`);
327
- nitro.options.runtimeConfig.public.sockets = {
328
- wsUrl
329
- };
330
- nitro.hooks.hook("close", async () => {
331
- await ws.close();
332
- await server.close();
333
- });
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}`);
334
478
  }
335
479
  });
336
- const instance = {
337
- send(data) {
338
- broker.broadcast(channel, data);
339
- return this;
340
- },
341
- addHandler(callback) {
342
- broker.addHandler(channel, callback);
343
- return this;
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
+ }
344
498
  }
345
- };
346
- if (handler) {
347
- instance.addHandler(handler);
348
- }
349
- return instance;
499
+ });
500
+ writeFile(path, { module: true, parsed });
501
+ return parsed;
350
502
  }
351
503
 
352
504
  const resolve = createResolver(import.meta.url).resolve;
505
+ const meta = {
506
+ moduleName: "nuxt-content-assets",
507
+ moduleKey: "contentAssets",
508
+ compatibility: {
509
+ nuxt: "^3.0.0"
510
+ }
511
+ };
512
+ const defaults = {
513
+ imageSize: "style",
514
+ contentExtensions: "md csv ya?ml json",
515
+ debug: false
516
+ };
353
517
  const module = defineNuxtModule({
354
- meta: {
355
- name: moduleName,
356
- configKey: moduleKey,
357
- compatibility: {
358
- nuxt: "^3.0.0"
359
- }
360
- },
518
+ meta,
361
519
  defaults,
362
520
  async setup(options, nuxt) {
363
521
  var _a, _b;
364
- const pluginPath = resolve("./runtime/plugin");
365
522
  const buildPath = nuxt.options.buildDir;
366
- const cachePath = Path.join(buildPath, "content-assets");
367
- const publicPath = Path.join(cachePath, "public");
368
- const indexPath = Path.join(cachePath, "assets.json");
523
+ const assetsPath = Path.join(buildPath, "content-assets");
524
+ const publicPath = Path.join(assetsPath, "public");
525
+ const contentPath = Path.join(buildPath, "content-cache/parsed");
369
526
  if (options.debug) {
370
527
  log("Removing cache folders...");
371
528
  }
372
529
  removeFolder(Path.join(buildPath, "content-cache"));
373
- removeFolder(cachePath);
374
- (_a = nuxt.options).content || (_a.content = {});
375
- if (nuxt.options.content) {
376
- (_b = nuxt.options.content).ignores || (_b.ignores = []);
530
+ removeFolder(assetsPath);
531
+ const { contentExtensions } = options;
532
+ if (contentExtensions) {
533
+ (_a = nuxt.options).content || (_a.content = {});
534
+ if (nuxt.options.content) {
535
+ (_b = nuxt.options.content).ignores || (_b.ignores = []);
536
+ }
537
+ const ignores = makeIgnores(contentExtensions);
538
+ nuxt.options.content?.ignores.push(ignores);
377
539
  }
378
- const ignores = getIgnores(options.contentExtensions);
379
- nuxt.options.content?.ignores.push(ignores);
380
540
  const imageFlags = matchTokens(options.imageSize);
381
- const sources = nuxt.options._layers.map((layer) => layer.config?.content?.sources).reduce((output, sources2) => {
541
+ const sources = Array.from(nuxt.options._layers).map((layer) => layer.config?.content?.sources).reduce((output, sources2) => {
382
542
  if (sources2) {
383
543
  Object.assign(output, sources2);
384
544
  }
@@ -393,34 +553,33 @@ const module = defineNuxtModule({
393
553
  };
394
554
  }
395
555
  }
396
- function updateAsset(src) {
397
- const { srcRel, srcAttr } = getAssetPaths(publicPath, src);
398
- const { width, height, ratio, query } = getAssetSizes(src, imageFlags);
399
- assets[srcRel] = {
400
- srcRel,
401
- srcAttr,
402
- width,
403
- height,
404
- ratio,
405
- query
406
- };
407
- saveAssets();
408
- return srcAttr;
409
- }
410
- function removeAsset(src) {
411
- const { srcRel, srcAttr } = getAssetPaths(publicPath, src);
412
- delete assets[srcRel];
413
- saveAssets();
414
- return srcAttr;
415
- }
416
- const saveAssets = debounce(() => {
417
- writeFile(indexPath, assets);
418
- }, 50);
419
- const assets = {};
556
+ const assets = makeAssetsManager(publicPath);
420
557
  function onAssetChange(event, absTrg) {
421
- const src = event === "update" ? updateAsset(absTrg) : removeAsset(absTrg);
422
- if (socket) {
423
- socket.send({ event, src });
558
+ let src = "";
559
+ let width;
560
+ let height;
561
+ if (event === "update") {
562
+ const oldAsset = isImage(absTrg) && imageFlags.length ? assets.getAsset(absTrg) : null;
563
+ const newAsset = assets.setAsset(absTrg);
564
+ width = newAsset.width;
565
+ height = newAsset.height;
566
+ if (oldAsset) {
567
+ if (oldAsset.width !== newAsset.width || oldAsset.height !== newAsset.height) {
568
+ newAsset.content.forEach(async (id) => {
569
+ const path = Path.join(contentPath, toPath(id));
570
+ rewriteContent(path, newAsset);
571
+ });
572
+ }
573
+ }
574
+ src = newAsset.srcAttr;
575
+ } else {
576
+ const asset = assets.removeAsset(absTrg);
577
+ if (asset) {
578
+ src = asset.srcAttr;
579
+ }
580
+ }
581
+ if (src && socket) {
582
+ socket.send({ event, src, width, height });
424
583
  }
425
584
  }
426
585
  addPlugin(resolve("./runtime/sockets/plugin"));
@@ -435,22 +594,24 @@ const module = defineNuxtModule({
435
594
  nuxt.hook("build:before", async function() {
436
595
  for (const [key, manager] of Object.entries(managers)) {
437
596
  const paths = await manager.init();
438
- paths.forEach((path) => updateAsset(path));
597
+ paths.forEach((path) => assets.setAsset(path));
439
598
  if (options.debug) {
440
599
  list(`Copied "${key}" assets`, paths.map((path) => Path.relative(publicPath, path)));
441
600
  }
442
601
  }
443
602
  });
603
+ const pluginPath = resolve("./runtime/content/plugin");
444
604
  const makeVar = (name, value) => `export const ${name} = ${JSON.stringify(value)};`;
445
605
  const virtualConfig = [
446
- makeVar("cachePath", cachePath),
606
+ makeVar("publicPath", publicPath),
607
+ makeVar("imageFlags", imageFlags),
447
608
  makeVar("debug", options.debug)
448
609
  ].join("\n");
449
610
  nuxt.hook("nitro:config", async (config) => {
450
611
  config.plugins || (config.plugins = []);
451
612
  config.plugins.push(pluginPath);
452
613
  config.virtual || (config.virtual = {});
453
- config.virtual[`#${moduleName}`] = () => {
614
+ config.virtual[`#${meta.moduleName}`] = () => {
454
615
  return virtualConfig;
455
616
  };
456
617
  config.publicAssets || (config.publicAssets = []);