Skip to content

Commit dc9a64e

Browse files
authored
feat: docs/** content hot reload (#9842)
* feat: docs/** content hot reload
1 parent f2eab2f commit dc9a64e

File tree

5 files changed

+118
-4
lines changed

5 files changed

+118
-4
lines changed

app/[[...path]]/layout.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ import 'prism-sentry/index.css';
22

33
import type {Metadata} from 'next';
44

5+
import {HotReload} from 'sentry-docs/components/hotReload';
6+
57
export const metadata: Metadata = {
68
title: {template: '%s | Sentry Documentation', default: 'Home'},
79
};
810

911
export default function DocsLayout({children}: {children: React.ReactNode}) {
10-
return <div>{children}</div>;
12+
return (
13+
<div>
14+
{children}
15+
<HotReload />
16+
</div>
17+
);
1118
}

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"url": "https://github.com/getsentry/sentry-docs/issues"
1212
},
1313
"scripts": {
14-
"dev": "concurrently \"yarn sidecar\" \"next dev\"",
14+
"dev": "concurrently \"yarn sidecar\" \"node ./src/hotReloadWatcher.mjs\" \"next dev\"",
1515
"build": "prisma generate && next build",
1616
"start": "next start",
1717
"migrate:dev": "dotenv -e .env.development -- yarn prisma migrate reset",
@@ -101,6 +101,7 @@
101101
"@types/node": "^20",
102102
"@types/react": "^18",
103103
"@types/react-dom": "^18",
104+
"@types/ws": "^8.5.10",
104105
"autoprefixer": "^10.4.17",
105106
"concurrently": "^8.2.2",
106107
"dotenv-cli": "^7.4.1",
@@ -115,7 +116,8 @@
115116
"prisma": "^5.8.1",
116117
"tailwindcss": "^3.4.1",
117118
"ts-node": "^10.9.1",
118-
"typescript": "^5"
119+
"typescript": "^5",
120+
"ws": "^8.16.0"
119121
},
120122
"volta": {
121123
"node": "20.11.0",

src/components/hotReload.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client';
2+
import {useEffect} from 'react';
3+
import {useRouter} from 'next/navigation';
4+
5+
function HotReload_() {
6+
const router = useRouter();
7+
let ws: WebSocket;
8+
const connect = () => {
9+
if (ws) {
10+
return;
11+
}
12+
ws = new WebSocket('ws://localhost:8080');
13+
ws.onopen = function open() {
14+
// do nothing
15+
};
16+
ws.onmessage = function incoming(msg) {
17+
if (msg.data === 'reload') {
18+
// eslint-disable-next-line no-console
19+
console.info('[REFRESHING]');
20+
router.refresh();
21+
}
22+
};
23+
ws.onerror = function error(...err) {
24+
// eslint-disable-next-line no-console
25+
console.error('Hot reload ws error', err);
26+
};
27+
ws.onclose = function close() {};
28+
};
29+
30+
useEffect(
31+
() => {
32+
connect();
33+
},
34+
// eslint-disable-next-line react-hooks/exhaustive-deps
35+
[]
36+
);
37+
38+
return null;
39+
}
40+
const noop = () => null;
41+
42+
/**
43+
* Hot reloads the page when notified by the server of a change under /docs/*
44+
*/
45+
export const HotReload = process.env.NODE_ENV === 'development' ? HotReload_ : noop;

src/hotReloadWatcher.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import path from 'path';
2+
import {watch} from 'node:fs/promises';
3+
import {WebSocketServer} from 'ws';
4+
5+
const watchedContent = new Set(['.mdx', '.md', '.png', '.jpg', '.jpeg', '.gif', '.svg']);
6+
7+
export const throttle = (fn, delay) => {
8+
let last = 0;
9+
return (...args) => {
10+
const now = Date.now();
11+
if (now - last < delay) {
12+
return;
13+
}
14+
last = now;
15+
return fn(...args);
16+
};
17+
};
18+
19+
const wss = new WebSocketServer({port: 8080});
20+
console.info('⚡️ Hot reload watcher listening on ws://localhost:8080');
21+
22+
wss.on('connection', async function onConnect(ws) {
23+
ws.on('error', err => {
24+
console.log('ws error', err);
25+
});
26+
ws.on('message', function incoming(_msg) {
27+
// no reason for the client to send messages for now
28+
});
29+
30+
const ac = new AbortController();
31+
const {signal} = ac;
32+
ws.on('close', () => ac.abort());
33+
34+
// avoid fileystem chatter when you save a file
35+
const sendReload = throttle(() => ws.send('reload'), 10);
36+
37+
try {
38+
const watcher = watch(path.join(import.meta.dirname, '..', 'docs'), {
39+
signal,
40+
recursive: true,
41+
});
42+
for await (const event of watcher) {
43+
if (watchedContent.has(path.extname(event.filename))) {
44+
sendReload();
45+
}
46+
}
47+
} catch (err) {
48+
if (err.name === 'AbortError') {
49+
return;
50+
}
51+
throw err;
52+
}
53+
});

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,13 @@
32953295
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc"
32963296
integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==
32973297

3298+
"@types/ws@^8.5.10":
3299+
version "8.5.10"
3300+
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
3301+
integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
3302+
dependencies:
3303+
"@types/node" "*"
3304+
32983305
"@types/yargs-parser@*":
32993306
version "21.0.3"
33003307
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@@ -11447,7 +11454,7 @@ ws@^7.4.6:
1144711454
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
1144811455
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
1144911456

11450-
ws@^8.11.0:
11457+
ws@^8.11.0, ws@^8.16.0:
1145111458
version "8.16.0"
1145211459
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
1145311460
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==

0 commit comments

Comments
 (0)