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.
Files changed (42) hide show
  1. package/README.md +2 -0
  2. package/dist/module.d.ts +21 -2
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +349 -168
  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 +2 -6
  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 -121
  34. package/dist/runtime/services/assets.d.ts +0 -25
  35. package/dist/runtime/services/assets.mjs +0 -23
  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/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
- contentExtensions: string | string[];
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 { ModuleOptions, _default as default };
29
+ export { _default as default };
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "1.1.1"
7
+ "version": "1.2.1"
8
8
  }
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,32 +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("/", ":");
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
- id,
118
- srcRel,
119
- srcAttr
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
- function getAssetSizes(srcAbs) {
123
- if (isImage(srcAbs)) {
124
- try {
125
- return getImageSize(srcAbs);
126
- } catch (err) {
127
- warn(`could not read image "${srcAbs}`);
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 makeStorage(source, key = "") {
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 = makeStorage(source, key);
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
- const moduleName = "nuxt-content-assets";
229
- const moduleKey = "contentAssets";
230
-
231
- function createWebSocket() {
232
- const wss = new WebSocketServer({ noServer: true });
233
- const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
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
- return {
259
- wss,
260
- serve,
261
- broadcast,
262
- addHandler,
263
- close: () => {
264
- wss.clients.forEach((client) => client.close());
265
- 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
+ }
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
- function isObject(data) {
271
- return data && typeof data === "object" && !Array.isArray(data);
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
- broadcast,
289
- addHandler
457
+ srcRel,
458
+ srcAttr
290
459
  };
291
460
  }
292
- const ws = createWebSocket();
293
- const broker = makeChannelBroker(ws);
294
- async function setupSocketServer(channel, handler) {
295
- const nuxt = useNuxt();
296
- nuxt.hook("nitro:init", async (nitro) => {
297
- if (!nuxt._socketServer) {
298
- const defaults = nuxt.options.runtimeConfig.content.watch.ws;
299
- const { server, url } = await listen(() => "Nuxt Content Assets", {
300
- port: defaults.port.port + 1,
301
- hostname: defaults.hostname,
302
- showURL: false
303
- });
304
- nuxt._socketServer = server;
305
- server.on("upgrade", ws.serve);
306
- const wsUrl = url.replace("http", "ws");
307
- log(`Websocket listening on "${wsUrl}"`);
308
- nitro.options.runtimeConfig.public.sockets = {
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
- const instance = {
318
- send(data) {
319
- broker.broadcast(channel, data);
320
- return this;
321
- },
322
- addHandler(callback) {
323
- broker.addHandler(channel, callback);
324
- 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
+ }
325
498
  }
326
- };
327
- if (handler) {
328
- instance.addHandler(handler);
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
- name: moduleName,
337
- configKey: moduleKey,
338
- compatibility: {
339
- nuxt: "^3.0.0"
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 cachePath = Path.join(buildPath, "content-assets");
348
- const publicPath = Path.join(cachePath, "public");
349
- const indexPath = Path.join(cachePath, "assets.json");
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(cachePath);
355
- (_a = nuxt.options).content || (_a.content = {});
356
- if (nuxt.options.content) {
357
- (_b = nuxt.options.content).ignores || (_b.ignores = []);
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
- function updateAsset(src) {
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
- const src = event === "update" ? updateAsset(absTrg) : removeAsset(absTrg);
400
- if (socket) {
401
- socket.send({ event, src });
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) => updateAsset(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("cachePath", cachePath),
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[`#${moduleName}`] = () => {
613
+ config.virtual[`#${meta.name}`] = () => {
433
614
  return virtualConfig;
434
615
  };
435
616
  config.publicAssets || (config.publicAssets = []);