Add dev server with livereload
npm run dev starts a dev server on port 8080 serving the test page and ribbit dist files. Watches src/ and test/integration/ for changes, rebuilds automatically, and notifies connected browsers to reload via EventSource on port 8081. The test page includes a livereload script that auto-reloads when the dev server signals a rebuild.
This commit is contained in:
parent
24560a4d21
commit
bfc20f56bf
|
|
@ -14,6 +14,7 @@
|
||||||
"build:core": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --sourcemap --outfile=dist/ribbit/ribbit-core.js",
|
"build:core": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --sourcemap --outfile=dist/ribbit/ribbit-core.js",
|
||||||
"build:core-min": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --minify --outfile=dist/ribbit/ribbit-core.min.js",
|
"build:core-min": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --minify --outfile=dist/ribbit/ribbit-core.min.js",
|
||||||
"build:css": "cp src/static/ribbit-core.css dist/ribbit/ && cp -r src/static/themes dist/ribbit/",
|
"build:css": "cp src/static/ribbit-core.css dist/ribbit/ && cp -r src/static/themes dist/ribbit/",
|
||||||
|
"dev": "npm run build && node test/integration/dev-server.js",
|
||||||
"test": "npm run build && jest --verbose",
|
"test": "npm run build && jest --verbose",
|
||||||
"test:integration": "npm run build && node test/integration/test.js && node test/integration/test_wysiwyg.js",
|
"test:integration": "npm run build && node test/integration/test.js && node test/integration/test_wysiwyg.js",
|
||||||
"test:coverage": "npm run build && jest --coverage"
|
"test:coverage": "npm run build && jest --coverage"
|
||||||
|
|
|
||||||
106
test/integration/dev-server.js
Normal file
106
test/integration/dev-server.js
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* Development server with livereload.
|
||||||
|
*
|
||||||
|
* Serves the test page and ribbit dist files. Watches src/ for
|
||||||
|
* changes, rebuilds automatically, and notifies connected browsers
|
||||||
|
* to reload via a simple EventSource stream.
|
||||||
|
*
|
||||||
|
* Run: npm run dev
|
||||||
|
*/
|
||||||
|
const { createServer } = require('./server');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const PORT = 8080;
|
||||||
|
const WATCH_DIRS = [
|
||||||
|
path.join(__dirname, '..', '..', 'src'),
|
||||||
|
path.join(__dirname, '..', '..', 'test', 'integration'),
|
||||||
|
];
|
||||||
|
const DEBOUNCE_MS = 300;
|
||||||
|
|
||||||
|
const server = createServer(PORT);
|
||||||
|
const reloadClients = [];
|
||||||
|
|
||||||
|
// Patch the server to add the livereload endpoint
|
||||||
|
const originalServer = require('http').createServer;
|
||||||
|
const httpServer = server._server || (() => {
|
||||||
|
// Access the internal server by starting and intercepting
|
||||||
|
let captured = null;
|
||||||
|
const origListen = require('http').Server.prototype.listen;
|
||||||
|
require('http').Server.prototype.listen = function (...args) {
|
||||||
|
captured = this;
|
||||||
|
return origListen.apply(this, args);
|
||||||
|
};
|
||||||
|
server.start();
|
||||||
|
require('http').Server.prototype.listen = origListen;
|
||||||
|
return captured;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Simpler approach: create a standalone livereload server
|
||||||
|
const reloadServer = require('http').createServer((request, response) => {
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
});
|
||||||
|
reloadClients.push(response);
|
||||||
|
request.on('close', () => {
|
||||||
|
const index = reloadClients.indexOf(response);
|
||||||
|
if (index >= 0) {
|
||||||
|
reloadClients.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function notifyReload() {
|
||||||
|
for (const client of reloadClients) {
|
||||||
|
client.write('data: reload\n\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuild() {
|
||||||
|
try {
|
||||||
|
console.log('\n🔨 Rebuilding...');
|
||||||
|
execSync('npm run build:js && npm run build:css', {
|
||||||
|
cwd: path.join(__dirname, '..', '..'),
|
||||||
|
stdio: 'pipe',
|
||||||
|
});
|
||||||
|
console.log('✅ Build complete');
|
||||||
|
notifyReload();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Build failed:', error.stderr?.toString().slice(0, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let debounceTimer = null;
|
||||||
|
function onFileChange(filename) {
|
||||||
|
if (debounceTimer) {
|
||||||
|
clearTimeout(debounceTimer);
|
||||||
|
}
|
||||||
|
console.log(`📝 Changed: ${filename}`);
|
||||||
|
debounceTimer = setTimeout(rebuild, DEBOUNCE_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch source directories
|
||||||
|
for (const directory of WATCH_DIRS) {
|
||||||
|
if (fs.existsSync(directory)) {
|
||||||
|
fs.watch(directory, { recursive: true }, (eventType, filename) => {
|
||||||
|
if (filename && !filename.includes('node_modules')) {
|
||||||
|
onFileChange(filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.start().then(() => {
|
||||||
|
reloadServer.listen(PORT + 1, () => {
|
||||||
|
console.log(`\n🐸 Ribbit dev server`);
|
||||||
|
console.log(` Editor: http://localhost:${PORT}`);
|
||||||
|
console.log(` Livereload: http://localhost:${PORT + 1} (EventSource)`);
|
||||||
|
console.log(` Watching: src/, test/integration/`);
|
||||||
|
console.log(`\n Add this to the page to enable livereload:`);
|
||||||
|
console.log(` <script>new EventSource('http://localhost:${PORT + 1}').onmessage = () => location.reload()</script>\n`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -42,5 +42,10 @@
|
||||||
editor.run();
|
editor.run();
|
||||||
window.__ribbitEditor = editor;
|
window.__ribbitEditor = editor;
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
// Livereload — connects to dev server's EventSource endpoint.
|
||||||
|
// Silently fails if the dev server isn't running.
|
||||||
|
try { new EventSource('http://localhost:8081').onmessage = () => location.reload(); } catch(e) {}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user