Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ui5-middleware-livereload): remove livereloaddeps #1117

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ui5-middleware-livereload/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ npm install ui5-middleware-livereload --save-dev

- debug: true|false
verbose logging
- extraExts: `string`, default: `jsx,ts,tsx,xml,json,properties`
- extraExts: `string`, default: `js,html,css,jsx,ts,tsx,xml,json,properties`
file extensions other than `js`, `html` and `css` to monitor for changes
- port: `integer`, default: an free port choosen from `35729` onwards
port the live reload server is started on
Expand Down
163 changes: 121 additions & 42 deletions packages/ui5-middleware-livereload/lib/livereload.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/* eslint-disable no-unused-vars, no-undef */
const connectLivereload = require("connect-livereload");
const livereload = require("livereload");
const path = require("path");
const fs = require("fs");
const os = require("os");
const portfinder = require("portfinder");
const chokidar = require("chokidar");
// eslint-disable-next-line no-redeclare
const WebSocket = require("ws");
const yargs = require("yargs");

/**
* @typedef {object} [configuration] configuration
Expand All @@ -12,6 +15,7 @@ const portfinder = require("portfinder");
* @property {string|yo<input|webapp>} [watchPath] path inside `$yourapp` the reload server monitors for changes
* @property {string} [exclusions] one or many `regex`. By default, this includes `.git/`, `.svn/`, and `.hg/`
* @property {boolean|yo<confirm|false>} [debug] see output
* @property {boolean} [usePolling] - enable polling for file changes
*/

/**
Expand All @@ -20,7 +24,7 @@ const portfinder = require("portfinder");
*
* @param {object} options the entered config option
* @param {number} defaultPort the port which is defaulted
* @returns {number} a port which is free
* @returns {Promise<number>} - a port which is free
*/
const getPortForLivereload = async (options, defaultPort) => {
if (options.configuration && options.configuration.port) {
Expand All @@ -40,6 +44,7 @@ const getPortForLivereload = async (options, defaultPort) => {
* <b>ATTENTION: this is a hack to be compatible with UI5 tooling 2.x and 3.x</b>
*
* @param {module:@ui5/fs.AbstractReader} collection Reader or Collection to read resources of the root project and its dependencies
* @param {boolean} skipFwkDeps whether to skip framework dependencies
* @returns {string[]} source paths
*/
const determineSourcePaths = (collection, skipFwkDeps) => {
Expand Down Expand Up @@ -123,53 +128,127 @@ module.exports = async ({ log, resources, options, middlewareUtil }) => {
} else if (exclusions) {
exclusions = [new RegExp(exclusions)];
}
let extraExts = options?.configuration?.extraExts || "jsx,ts,tsx,xml,json,properties";
let debug = options?.configuration?.debug;
let usePolling = options?.configuration?.usePolling;

let serverOptions = {
debug: debug,
extraExts: extraExts ? extraExts.split(",") : undefined,
port: port,
exclusions: exclusions,
usePolling: usePolling,
};
const extraExts = options?.configuration?.extraExts || "js,html,css,jsx,ts,tsx,xml,json,properties";
const debug = options?.configuration?.debug;
const usePolling = options?.configuration?.usePolling;

const cli = require("yargs");
if (cli.argv.h2) {
const os = require("os");
const fs = require("fs");
// SSL Configuration via command-line arguments
const cli = yargs(process.argv.slice(2)).argv;

sslKeyPath = cli.argv.key ? cli.argv.key : path.join(os.homedir(), ".ui5", "server", "server.key");
sslCertPath = cli.argv.cert ? cli.argv.cert : path.join(os.homedir(), ".ui5", "server", "server.crt");
debug ? log.info(`Livereload using SSL key ${sslKeyPath}`) : null;
debug ? log.info(`Livereload using SSL certificate ${sslCertPath}`) : null;

serverOptions.https = {
key: fs.readFileSync(sslKeyPath),
cert: fs.readFileSync(sslCertPath),
};
let serverOptions = {};
let useHttps = false;
if (cli.h2) {
useHttps = true;
const sslKeyPath = cli.key ? cli.key : path.join(os.homedir(), ".ui5", "server", "server.key");
const sslCertPath = cli.cert ? cli.cert : path.join(os.homedir(), ".ui5", "server", "server.crt");
if (fs.existsSync(sslKeyPath) && fs.existsSync(sslCertPath)) {
serverOptions = {
key: fs.readFileSync(sslKeyPath),
cert: fs.readFileSync(sslCertPath),
};
debug && log.info(`Livereload using SSL key ${sslKeyPath} and certificate ${sslCertPath}`);
} else {
log.warn("SSL key or certificate not found, falling back to HTTP.");
}
}

const livereloadServer = livereload.createServer(serverOptions, () => {
log.info("Livereload server started!");
// Build array of file extensions to watch
const extsToWatch = extraExts.split(",");

// Set up chokidar watcher
const watcher = chokidar.watch(watchPath, {
ignored: exclusions,
ignoreInitial: true,
usePolling: usePolling,
cwd: cwd,
followSymlinks: true,
depth: Infinity,
});

if (Array.isArray(watchPath)) {
let watchPaths = [];
for (let i = 0; i < watchPath.length; i++) {
watchPaths.push(path.resolve(cwd, watchPath[i]));
}
debug ? log.info(`Livereload connecting to port ${port} for paths ${watchPaths}`) : null;
livereloadServer.watch(watchPaths);
// Set up WebSocket server
let server;
if (useHttps && Object.keys(serverOptions).length > 0) {
// HTTPS Server
server = require("https").createServer(serverOptions);
} else {
debug ? log.info(`Livereload connecting to port ${port} for path ${watchPath}`) : null;
livereloadServer.watch(path.resolve(cwd, watchPath));
// HTTP Server
server = require("http").createServer();
}
server.listen(port, () => {
debug && log.info(`Livereload server started on port ${port}`);
});

// connect-livereload already holds the
// method sig (req, res, next)
return connectLivereload({
port: port,
const wss = new WebSocket.Server({ server });

wss.on("connection", (ws) => {
debug && log.info("WebSocket client connected");
});

watcher.on("all", (event, filePath) => {
// Check if the changed file matches the extensions
const ext = path.extname(filePath).substring(1);
if (extsToWatch.includes(ext)) {
debug && log.info(`File ${event}: ${filePath}`);
// Notify all connected clients to reload
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("reload");
}
});
}
});

// Middleware function to inject the client-side script
return (req, res, next) => {
// Only inject into HTML responses
if (res._livereloadInjected) {
return next();
}

// Intercept write and end methods
const originalWrite = res.write;
const originalEnd = res.end;

let body = "";

res.write = function (chunk) {
body += chunk instanceof Buffer ? chunk.toString() : chunk;
};

res.end = function (chunk) {
if (chunk) {
body += chunk instanceof Buffer ? chunk.toString() : chunk;
}

// Check if response is HTML
if (res.getHeader("Content-Type") && res.getHeader("Content-Type").includes("text/html")) {
// Determine protocol
const protocol = useHttps ? "wss" : "ws";
// Inject the client-side script
const script = `
<script>
(function() {
var protocol = '${protocol}';
var port = ${port};
var host = location.hostname;
var socket = new WebSocket(protocol + '://' + host + ':' + port);
socket.onmessage = function(event) {
if (event.data === 'reload') {
location.reload();
}
};
})();
</script>
`;
body = body.replace(/<\/body>/i, script + "</body>");
}

res._livereloadInjected = true;
res.write = originalWrite;
res.end = originalEnd;
res.end(body);
};

next();
};
};
6 changes: 3 additions & 3 deletions packages/ui5-middleware-livereload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"lint": "eslint lib"
},
"dependencies": {
"connect-livereload": "^0.6.1",
"livereload": "^0.9.3",
"portfinder": "^1.0.32",
"yargs": "^17.7.2"
"yargs": "^17.7.2",
"chokidar": "^4.0.1",
"ws": "^8.18.0"
}
}
Loading
Loading