nuxt-content-assets 0.9.0-alpha → 0.9.0-beta

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 CHANGED
@@ -43,7 +43,7 @@ Almost as much as being in the sea!
43
43
  <video src="media/seaside.mp4"></video>
44
44
  ```
45
45
 
46
- The module supports a variety of [common tags](#how-it-works) and has additional goodies such as [image sizing](#image-sizing) and [live-reload](#live-reload).
46
+ The module [processes assets](#how-it-works) and serves them together with your content, adding features such as [image sizing](#image-sizing) and [live-reload](#live-reload).
47
47
 
48
48
  ## Demo
49
49
 
@@ -95,7 +95,7 @@ Use relative paths anywhere within your documents:
95
95
  <video src="media/video.mp4" />
96
96
  ```
97
97
 
98
- Relative paths can be defined in frontmatter, as long as they are the only value:
98
+ Relative paths can be defined in frontmatter as long as they are the only value:
99
99
 
100
100
  ```mdx
101
101
  ---
@@ -110,11 +110,11 @@ images:
110
110
  These values can then be passed to components:
111
111
 
112
112
  ```markdown
113
- ::gallery{:data="images"}
113
+ ::ImageGallery{:data="images"}
114
114
  ::
115
115
  ```
116
116
 
117
- See the [Demo](demo/content/recipes/index.md) for a component example.
117
+ See the Demo for [markup](demo/content/recipes/index.md) and [Demo](demo/components/content/ImageGallery.vue) examples.
118
118
 
119
119
  ### Live reload
120
120
 
@@ -154,13 +154,13 @@ Nuxt Content Assets works by serving a _copy_ of your assets using [Nitro](https
154
154
 
155
155
  When Nuxt builds, the following happens:
156
156
 
157
- - all content sources are scanned for valid assets
157
+ - content sources are scanned for valid assets
158
158
  - found assets are copied to a temporary build folder
159
- - relative paths in markdown are rewritten to point at this folder
159
+ - any relative asset paths are rewritten as absolute
160
160
  - metadata such as image size is written to a lookup file
161
161
  - finally, Nitro serves the folder for public access
162
162
 
163
- Note only specific tags and attributes are targeted in the parsing phase for rewriting:
163
+ Note that in the rewriting phase, only specific tags and attributes are targeted :
164
164
 
165
165
  ```html
166
166
  <a href="...">
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.9.0-alpha"
7
+ "version": "0.9.0-beta"
8
8
  }
package/dist/module.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  import * as Fs from 'fs';
2
2
  import * as Path from 'path';
3
3
  import Path__default from 'path';
4
- import { createResolver, defineNuxtModule, addPlugin } from '@nuxt/kit';
4
+ import { useNuxt, createResolver, defineNuxtModule, addPlugin } from '@nuxt/kit';
5
5
  import debounce from 'debounce';
6
6
  import getImageSize from 'image-size';
7
7
  import { createStorage } from 'unstorage';
8
8
  import githubDriver from 'unstorage/drivers/github';
9
9
  import fsDriver from 'unstorage/drivers/fs';
10
10
  import { hash } from 'ohash';
11
- import { WebSocketServer } from 'ws';
12
11
  import { listen } from 'listhen';
12
+ import { WebSocketServer, WebSocket } from 'ws';
13
13
 
14
14
  function matchWords(value) {
15
15
  return typeof value === "string" ? value.match(/\w+/g) || [] : [];
@@ -228,41 +228,66 @@ function interpolatePattern(pattern, src, warn = false) {
228
228
  function createWebSocket() {
229
229
  const wss = new WebSocketServer({ noServer: true });
230
230
  const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
231
- const broadcast = (data, channel = "*") => {
232
- data = JSON.stringify({ channel, data });
231
+ const broadcast = (data) => {
232
+ data = JSON.stringify(data);
233
233
  for (const client of wss.clients) {
234
- try {
234
+ if (client.readyState === WebSocket.OPEN) {
235
235
  client.send(data);
236
- } catch (err) {
237
236
  }
238
237
  }
239
238
  };
240
239
  const handlers = [];
241
- const onMessage = (channel, callback) => {
242
- handlers.push({ channel, callback });
240
+ const addHandler = (callback) => {
241
+ handlers.push(callback);
243
242
  };
244
- wss.on("connection", (client) => {
245
- client.addEventListener("message", (event) => {
243
+ wss.on("connection", (socket) => {
244
+ socket.addEventListener("message", (event) => {
245
+ let data;
246
246
  try {
247
- const { channel, data } = JSON.parse(event.data || "{}");
248
- handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(data));
247
+ data = JSON.parse(event.data || "{}");
249
248
  } catch (err) {
250
249
  }
250
+ if (data) {
251
+ handlers.forEach((callback) => callback(data));
252
+ }
251
253
  });
252
254
  });
253
255
  return {
254
256
  wss,
255
257
  serve,
256
258
  broadcast,
257
- onMessage,
259
+ addHandler,
258
260
  close: () => {
259
261
  wss.clients.forEach((client) => client.close());
260
262
  return new Promise((resolve) => wss.close(resolve));
261
263
  }
262
264
  };
263
265
  }
266
+
267
+ function isObject(data) {
268
+ return data && typeof data === "object" && !Array.isArray(data);
269
+ }
270
+ function makeChannelBroker(ws2) {
271
+ const handlers = [];
272
+ const broadcast = (channel, data) => {
273
+ ws2.broadcast({ channel, data });
274
+ };
275
+ const addHandler = (channel, callback) => {
276
+ handlers.push({ channel, callback });
277
+ };
278
+ ws2.addHandler(function(message) {
279
+ if (isObject(message)) {
280
+ const { channel } = message;
281
+ handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(message));
282
+ }
283
+ });
284
+ return {
285
+ broadcast,
286
+ addHandler
287
+ };
288
+ }
264
289
  const ws = createWebSocket();
265
- let initialized = false;
290
+ const broker = makeChannelBroker(ws);
266
291
  const defaults = {
267
292
  port: {
268
293
  port: 4001,
@@ -271,11 +296,12 @@ const defaults = {
271
296
  hostname: "localhost",
272
297
  showURL: false
273
298
  };
274
- function useSocketServer(nuxt, channel, onMessage) {
299
+ async function setupSocketServer(channel, handler) {
300
+ const nuxt = useNuxt();
275
301
  nuxt.hook("nitro:init", async (nitro) => {
276
- if (!initialized) {
277
- initialized = true;
302
+ if (!nuxt._socketServer) {
278
303
  const { server, url } = await listen(() => "Nuxt Sockets", defaults);
304
+ nuxt._socketServer = server;
279
305
  server.on("upgrade", ws.serve);
280
306
  nitro.options.runtimeConfig.public.sockets = {
281
307
  wsUrl: url.replace("http", "ws")
@@ -288,16 +314,16 @@ function useSocketServer(nuxt, channel, onMessage) {
288
314
  });
289
315
  const instance = {
290
316
  send(data) {
291
- ws.broadcast(data, channel);
317
+ broker.broadcast(channel, data);
292
318
  return this;
293
319
  },
294
- onMessage(callback) {
295
- ws.onMessage(channel, callback);
320
+ addHandler(callback) {
321
+ broker.addHandler(channel, callback);
296
322
  return this;
297
323
  }
298
324
  };
299
- if (onMessage) {
300
- instance.onMessage(onMessage);
325
+ if (handler) {
326
+ instance.addHandler(handler);
301
327
  }
302
328
  return instance;
303
329
  }
@@ -332,7 +358,7 @@ const module = defineNuxtModule({
332
358
  if (nuxt.options.content) {
333
359
  (_b = nuxt.options.content).ignores || (_b.ignores = []);
334
360
  }
335
- nuxt.options.content?.ignores.push("^((?!(md|json|yaml|csv)).)*$");
361
+ nuxt.options.content?.ignores.push("^((?!(mdx?|json|ya?ml|csv)).)*$");
336
362
  const output = options.output || defaults$1.assetsDir;
337
363
  const matches = output.match(/([^[]+)(.*)?/);
338
364
  const assetsPattern = (matches ? matches[2] : "") || defaults$1.assetsPattern;
@@ -353,8 +379,8 @@ const module = defineNuxtModule({
353
379
  };
354
380
  }
355
381
  }
356
- addPlugin(resolve("./runtime/watcher"));
357
- const socket = nuxt.options.dev ? useSocketServer(nuxt, "content-assets") : null;
382
+ addPlugin(resolve("./runtime/sockets/plugin"));
383
+ const socket = nuxt.options.dev ? await setupSocketServer("content-assets") : null;
358
384
  function removeAsset(src) {
359
385
  const srcRel = Path.relative(publicPath, src);
360
386
  delete assets[srcRel];
@@ -382,9 +408,9 @@ const module = defineNuxtModule({
382
408
  return srcAttr;
383
409
  }
384
410
  function watchAsset(event, absTrg) {
385
- const srcAttr = event === "update" ? updateAsset(absTrg) : removeAsset(absTrg);
411
+ const src = event === "update" ? updateAsset(absTrg) : removeAsset(absTrg);
386
412
  if (socket) {
387
- socket.send({ event: "update", src: srcAttr });
413
+ socket.send({ event, src });
388
414
  }
389
415
  }
390
416
  const saveAssets = debounce(() => {
@@ -0,0 +1,2 @@
1
+ import { Callback, SocketInstance } from '../../types';
2
+ export declare function useSockets(channel: string, callback?: Callback): Promise<SocketInstance | null>;
@@ -0,0 +1,12 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ export function useSockets(channel, callback) {
3
+ const url = useRuntimeConfig().public.sockets?.wsUrl;
4
+ return new Promise(function(resolve) {
5
+ if (process.client && url) {
6
+ return import("./setup").then(({ setupSocketClient }) => {
7
+ return resolve(setupSocketClient(channel, callback));
8
+ });
9
+ }
10
+ resolve(null);
11
+ });
12
+ }
@@ -0,0 +1,5 @@
1
+ import { Callback } from '../../types';
2
+ export declare function createWebSocket(): {
3
+ send: (data: any) => void;
4
+ addHandler(callback: Callback): void;
5
+ } | null;
@@ -7,10 +7,10 @@ const logger = {
7
7
  warn: (...args) => console.warn(plugin, ...args)
8
8
  };
9
9
  let ws;
10
- export function useSocket(channel, callback) {
10
+ export function createWebSocket() {
11
11
  if (!window.WebSocket) {
12
- logger.warn("Unable to hot-reload images, your browser does not support WebSocket");
13
- return;
12
+ logger.warn("Your browser does not support WebSocket");
13
+ return null;
14
14
  }
15
15
  const onOpen = () => logger.log("WS connected!");
16
16
  const onError = (e) => {
@@ -19,30 +19,35 @@ export function useSocket(channel, callback) {
19
19
  connect(true);
20
20
  break;
21
21
  default:
22
- logger.warn("WS Error:", e);
22
+ logger.warn("Socket error:", e);
23
23
  break;
24
24
  }
25
25
  };
26
26
  const onClose = (e) => {
27
27
  if (e.code === 1e3 || e.code === 1005) {
28
- logger.log("WS closed!");
28
+ logger.log("Socket closed");
29
29
  } else {
30
30
  connect(true);
31
31
  }
32
32
  };
33
+ const handlers = [];
33
34
  const onMessage = (message) => {
35
+ let data;
34
36
  try {
35
- const data = JSON.parse(message.data);
36
- if (channel === data.channel) {
37
- return callback(data);
38
- }
37
+ data = JSON.parse(message.data);
39
38
  } catch (err) {
40
39
  logger.warn("Error parsing message:", message.data);
40
+ return;
41
41
  }
42
+ handlers.forEach((handler) => {
43
+ if (typeof handler === "function") {
44
+ handler(data);
45
+ }
46
+ });
42
47
  };
43
48
  const send = (data) => {
44
49
  if (ws) {
45
- ws.send(JSON.stringify({ channel, data }));
50
+ ws.send(JSON.stringify(data));
46
51
  }
47
52
  };
48
53
  const connect = (retry = false) => {
@@ -61,7 +66,7 @@ export function useSocket(channel, callback) {
61
66
  const url = useRuntimeConfig().public.sockets?.wsUrl;
62
67
  if (url) {
63
68
  const wsUrl = `${url}ws`;
64
- logger.log(`watching for image updates on ${wsUrl}`);
69
+ logger.log(`Running on ${wsUrl}`);
65
70
  ws = new WebSocket(wsUrl);
66
71
  ws.onopen = onOpen;
67
72
  ws.onmessage = onMessage;
@@ -69,9 +74,13 @@ export function useSocket(channel, callback) {
69
74
  ws.onclose = onClose;
70
75
  }
71
76
  };
72
- connect();
77
+ if (!ws) {
78
+ connect();
79
+ }
73
80
  return {
74
- connect,
75
- send
81
+ send,
82
+ addHandler(callback) {
83
+ handlers.push(callback);
84
+ }
76
85
  };
77
86
  }
@@ -0,0 +1,22 @@
1
+ import { defineNuxtPlugin } from "#imports";
2
+ import { useSockets } from "./composable.mjs";
3
+ export default defineNuxtPlugin(async () => {
4
+ if (process.client) {
5
+ const sockets = await useSockets("content-assets");
6
+ if (sockets) {
7
+ sockets.addHandler(({ data }) => {
8
+ const { event, src } = data;
9
+ if (src) {
10
+ const isUpdate = event === "update";
11
+ document.querySelectorAll(`img[src^="${src}"]`).forEach((el) => {
12
+ const img = el;
13
+ img.style.opacity = isUpdate ? "1" : "0.2";
14
+ if (isUpdate) {
15
+ img.setAttribute("src", `${src}?${(/* @__PURE__ */ new Date()).getTime()}`);
16
+ }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ }
22
+ });
@@ -0,0 +1,2 @@
1
+ import { Callback, SocketInstance } from '../../types';
2
+ export declare function setupSocketClient(channel: string, callback?: Callback): SocketInstance | null;
@@ -0,0 +1,26 @@
1
+ import { createWebSocket } from "./factory.mjs";
2
+ const client = createWebSocket();
3
+ export function setupSocketClient(channel, callback) {
4
+ const instance = {
5
+ addHandler(callback2) {
6
+ if (client && typeof callback2 === "function") {
7
+ client.addHandler((data) => {
8
+ if (data.channel === channel) {
9
+ return callback2(data);
10
+ }
11
+ });
12
+ }
13
+ return this;
14
+ },
15
+ send(data) {
16
+ if (client) {
17
+ client.send({ channel, data });
18
+ }
19
+ return this;
20
+ }
21
+ };
22
+ if (callback) {
23
+ instance.addHandler(callback);
24
+ }
25
+ return instance;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-content-assets",
3
- "version": "0.9.0-alpha",
3
+ "version": "0.9.0-beta",
4
4
  "description": "Enable locally-located assets in Nuxt Content",
5
5
  "repository": "davestewart/nuxt-content-assets",
6
6
  "license": "MIT",
@@ -31,7 +31,6 @@
31
31
  "release:dry": "npm run lint && npm run test && npm run build && npm publish --dry-run"
32
32
  },
33
33
  "dependencies": {
34
- "@davestewart/nuxt-sockets": "^0.1.0",
35
34
  "@nuxt/kit": "^3.3.2",
36
35
  "debounce": "^1.2.1",
37
36
  "glob": "^9.3.2",
@@ -1,2 +0,0 @@
1
- import { Callback } from '../../utils';
2
- export declare function useSocketClient(channel: string, onMessage: Callback): void;
@@ -1,9 +0,0 @@
1
- import { useRuntimeConfig } from "#imports";
2
- export function useSocketClient(channel, onMessage) {
3
- const url = useRuntimeConfig().public.sockets?.wsUrl;
4
- if (process.client && url) {
5
- import("./composable").then(({ useSocket }) => {
6
- useSocket(channel, onMessage);
7
- });
8
- }
9
- }
@@ -1,4 +0,0 @@
1
- export declare function useSocket(channel: string, callback: (data: any) => void): {
2
- connect: (retry?: boolean) => void;
3
- send: (data: any) => void;
4
- } | undefined;
@@ -1,23 +0,0 @@
1
- /// <reference types="ws" />
2
- /// <reference types="node" />
3
- import type { IncomingMessage } from 'http';
4
- import { Nuxt } from '@nuxt/schema';
5
- export type Callback = (data: any) => void;
6
- export type Handler = {
7
- channel: string;
8
- callback: Callback;
9
- };
10
- /**
11
- * WebSocket server useful for live content reload.
12
- */
13
- export declare function createWebSocket(): {
14
- wss: import("ws").Server<import("ws").WebSocket>;
15
- serve: (req: IncomingMessage, socket?: import("net").Socket, head?: any) => void;
16
- broadcast: (data: any, channel?: string) => void;
17
- onMessage: (channel: string, callback: Callback) => void;
18
- close: () => Promise<unknown>;
19
- };
20
- export declare function useSocketServer(nuxt: Nuxt, channel: string, onMessage?: Callback): {
21
- send(data: any): any;
22
- onMessage(callback: Callback): any;
23
- };
@@ -1,78 +0,0 @@
1
- import { WebSocketServer } from "ws";
2
- import { listen } from "listhen";
3
- export function createWebSocket() {
4
- const wss = new WebSocketServer({ noServer: true });
5
- const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
6
- const broadcast = (data, channel = "*") => {
7
- data = JSON.stringify({ channel, data });
8
- for (const client of wss.clients) {
9
- try {
10
- client.send(data);
11
- } catch (err) {
12
- }
13
- }
14
- };
15
- const handlers = [];
16
- const onMessage = (channel, callback) => {
17
- handlers.push({ channel, callback });
18
- };
19
- wss.on("connection", (client) => {
20
- client.addEventListener("message", (event) => {
21
- try {
22
- const { channel, data } = JSON.parse(event.data || "{}");
23
- handlers.filter((handler) => handler.channel === channel || handler.channel === "*").forEach((handler) => handler.callback(data));
24
- } catch (err) {
25
- }
26
- });
27
- });
28
- return {
29
- wss,
30
- serve,
31
- broadcast,
32
- onMessage,
33
- close: () => {
34
- wss.clients.forEach((client) => client.close());
35
- return new Promise((resolve) => wss.close(resolve));
36
- }
37
- };
38
- }
39
- const ws = createWebSocket();
40
- let initialized = false;
41
- const defaults = {
42
- port: {
43
- port: 4001,
44
- portRange: [4001, 4040]
45
- },
46
- hostname: "localhost",
47
- showURL: false
48
- };
49
- export function useSocketServer(nuxt, channel, onMessage) {
50
- nuxt.hook("nitro:init", async (nitro) => {
51
- if (!initialized) {
52
- initialized = true;
53
- const { server, url } = await listen(() => "Nuxt Sockets", defaults);
54
- server.on("upgrade", ws.serve);
55
- nitro.options.runtimeConfig.public.sockets = {
56
- wsUrl: url.replace("http", "ws")
57
- };
58
- nitro.hooks.hook("close", async () => {
59
- await ws.close();
60
- await server.close();
61
- });
62
- }
63
- });
64
- const instance = {
65
- send(data) {
66
- ws.broadcast(data, channel);
67
- return this;
68
- },
69
- onMessage(callback) {
70
- ws.onMessage(channel, callback);
71
- return this;
72
- }
73
- };
74
- if (onMessage) {
75
- instance.onMessage(onMessage);
76
- }
77
- return instance;
78
- }
@@ -1,18 +0,0 @@
1
- import { defineNuxtPlugin } from "#imports";
2
- import { useSocketClient } from "./services/sockets/client.mjs";
3
- export default defineNuxtPlugin(async () => {
4
- if (process.client) {
5
- void useSocketClient("content-assets", ({ data }) => {
6
- const { event, src } = data;
7
- if (src) {
8
- const isUpdate = event === "update";
9
- document.querySelectorAll(`img[src^="${src}"]`).forEach((img) => {
10
- img.style.opacity = isUpdate ? "1" : "0.2";
11
- if (isUpdate) {
12
- img.setAttribute("src", src);
13
- }
14
- });
15
- }
16
- });
17
- }
18
- });