From 7b8796c345dbf4e5035a5bd4b47230fcc5814a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:43:00 +0100 Subject: [PATCH 1/3] chore: first working version --- packages/ui5-middleware-livereload/README.md | 2 +- .../lib/livereload.js | 118 ++++++++++++------ .../ui5-middleware-livereload/package.json | 6 +- pnpm-lock.yaml | 102 +++++---------- 4 files changed, 114 insertions(+), 114 deletions(-) diff --git a/packages/ui5-middleware-livereload/README.md b/packages/ui5-middleware-livereload/README.md index 435c48cf..229ba9fb 100644 --- a/packages/ui5-middleware-livereload/README.md +++ b/packages/ui5-middleware-livereload/README.md @@ -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 diff --git a/packages/ui5-middleware-livereload/lib/livereload.js b/packages/ui5-middleware-livereload/lib/livereload.js index 41d3f5c6..56c26293 100644 --- a/packages/ui5-middleware-livereload/lib/livereload.js +++ b/packages/ui5-middleware-livereload/lib/livereload.js @@ -1,9 +1,10 @@ /* 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 portfinder = require("portfinder"); +const chokidar = require("chokidar"); +// eslint-disable-next-line no-redeclare +const WebSocket = require("ws"); /** * @typedef {object} [configuration] configuration @@ -123,53 +124,90 @@ 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 extraExts = options?.configuration?.extraExts || "js,html,css,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, - }; + // Set up WebSocket server + const wss = new WebSocket.Server({ port }); + wss.on("connection", (ws) => { + debug && log.info("WebSocket client connected"); + }); - const cli = require("yargs"); - if (cli.argv.h2) { - const os = require("os"); - const fs = require("fs"); + // Build array of file extensions to watch + const extsToWatch = extraExts.split(","); - 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; + // Prepare glob patterns for chokidar + const globPatterns = extsToWatch.map((ext) => `**/*.${ext}`); - serverOptions.https = { - key: fs.readFileSync(sslKeyPath), - cert: fs.readFileSync(sslCertPath), - }; - } + // Set up chokidar watcher + const watcher = chokidar.watch(watchPath, { + ignored: exclusions, + ignoreInitial: true, + usePolling: usePolling, + cwd: cwd, + followSymlinks: true, + depth: Infinity, + }); - const livereloadServer = livereload.createServer(serverOptions, () => { - log.info("Livereload server started!"); + watcher.on("all", (event, filePath) => { + debug && log.info(`File ${event}: ${filePath}`); + // Notify all connected clients to reload + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.send("reload"); + } + }); }); - if (Array.isArray(watchPath)) { - let watchPaths = []; - for (let i = 0; i < watchPath.length; i++) { - watchPaths.push(path.resolve(cwd, watchPath[i])); + // Middleware function to inject the client-side script + return (req, res, next) => { + // Only inject into HTML responses + if (res._livereloadInjected) { + return next(); } - debug ? log.info(`Livereload connecting to port ${port} for paths ${watchPaths}`) : null; - livereloadServer.watch(watchPaths); - } else { - debug ? log.info(`Livereload connecting to port ${port} for path ${watchPath}`) : null; - livereloadServer.watch(path.resolve(cwd, watchPath)); - } - // connect-livereload already holds the - // method sig (req, res, next) - return connectLivereload({ - port: port, - }); + // Intercept write and end methods + const originalWrite = res.write; + const originalEnd = res.end; + + let body = ""; + + res.write = function (chunk) { + body += chunk.toString(); + }; + + res.end = function (chunk) { + if (chunk) { + body += chunk.toString(); + } + + // Check if response is HTML + if (res.getHeader("Content-Type") && res.getHeader("Content-Type").includes("text/html")) { + // Inject the client-side script + const script = ` + + `; + body = body.replace(/<\/body>/i, script + ""); + } + + // Remove the line that sets Content-Length + // res.setHeader("Content-Length", Buffer.byteLength(body)); + res._livereloadInjected = true; + res.write = originalWrite; + res.end = originalEnd; + res.end(body); + }; + + next(); + }; }; diff --git a/packages/ui5-middleware-livereload/package.json b/packages/ui5-middleware-livereload/package.json index 05e9beed..219a9fbd 100644 --- a/packages/ui5-middleware-livereload/package.json +++ b/packages/ui5-middleware-livereload/package.json @@ -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" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d6df42b..73cb0100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,15 +209,15 @@ importers: packages/ui5-middleware-livereload: dependencies: - connect-livereload: - specifier: ^0.6.1 - version: 0.6.1 - livereload: - specifier: ^0.9.3 - version: 0.9.3 + chokidar: + specifier: ^4.0.1 + version: 4.0.1 portfinder: specifier: ^1.0.32 version: 1.0.32 + ws: + specifier: ^8.18.0 + version: 8.18.0 yargs: specifier: ^17.7.2 version: 17.7.2 @@ -724,7 +724,7 @@ importers: version: 2.3.0 axios: specifier: ^1.7.7 - version: 1.7.7 + version: 1.7.7(debug@4.3.7) cmis: specifier: ^1.0.3 version: 1.0.3 @@ -4260,9 +4260,6 @@ packages: resolution: {integrity: sha512-hdBG8nXop42y2gWCqOV8y1O3uVk4cIU+SoxLCPyCUKRImyPiScoNiSulpHjoktRU1BdI0UzoUdxUa87thrcmHw==} engines: {node: '>= 0.8.0'} - connect-livereload@0.6.1: - resolution: {integrity: sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==} - connect@3.6.5: resolution: {integrity: sha512-B+WTJ0bDgjQugnbNF7fWGvwEgTj9Isdk3Y7yTZlgCuVe+hpl/do8frEMeimx7sRMPW3oZA+EsC9uDZL8MaaAwQ==} engines: {node: '>= 0.10.0'} @@ -6651,14 +6648,6 @@ packages: lit-html@2.8.0: resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==} - livereload-js@3.4.1: - resolution: {integrity: sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==} - - livereload@0.9.3: - resolution: {integrity: sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==} - engines: {node: '>=8.0.0'} - hasBin: true - load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -7498,9 +7487,6 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - opts@2.0.2: - resolution: {integrity: sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==} - ora@5.3.0: resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} engines: {node: '>=10'} @@ -13070,7 +13056,7 @@ snapshots: agent-base@7.1.1: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -13344,14 +13330,6 @@ snapshots: transitivePeerDependencies: - debug - axios@1.7.7: - dependencies: - follow-redirects: 1.15.6(debug@4.3.2) - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - axios@1.7.7(debug@4.3.7): dependencies: follow-redirects: 1.15.6(debug@4.3.7) @@ -14129,8 +14107,6 @@ snapshots: transitivePeerDependencies: - supports-color - connect-livereload@0.6.1: {} - connect@3.6.5: dependencies: debug: 2.6.9 @@ -14805,7 +14781,7 @@ snapshots: base64id: 2.0.0 cookie: 0.4.2 cors: 2.8.5 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) engine.io-parser: 5.2.3 ws: 8.17.1 transitivePeerDependencies: @@ -15265,7 +15241,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -15728,7 +15704,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) fs-extra: 11.2.0 transitivePeerDependencies: - supports-color @@ -16089,7 +16065,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -16108,7 +16084,7 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.6(debug@4.3.2) + follow-redirects: 1.15.6(debug@4.3.7) requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -16142,7 +16118,7 @@ snapshots: https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -16583,7 +16559,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -17088,7 +17064,7 @@ snapshots: dependencies: chalk: 5.3.0 commander: 12.1.0 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) execa: 8.0.1 lilconfig: 3.1.2 listr2: 8.2.4 @@ -17112,18 +17088,6 @@ snapshots: dependencies: '@types/trusted-types': 2.0.7 - livereload-js@3.4.1: {} - - livereload@0.9.3: - dependencies: - chokidar: 3.6.0 - livereload-js: 3.4.1 - opts: 2.0.2 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 @@ -17246,7 +17210,7 @@ snapshots: log4js@6.9.1: dependencies: date-format: 4.0.14 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) flatted: 3.3.1 rfdc: 1.4.1 streamroller: 3.1.5 @@ -17657,7 +17621,7 @@ snapshots: nock@13.5.5: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) json-stringify-safe: 5.0.1 propagate: 2.0.1 transitivePeerDependencies: @@ -17907,7 +17871,7 @@ snapshots: '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.0-rc.46 '@zkochan/js-yaml': 0.0.7 - axios: 1.7.7 + axios: 1.7.7(debug@4.3.7) chalk: 4.1.2 cli-cursor: 3.1.0 cli-spinners: 2.6.1 @@ -18044,8 +18008,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - opts@2.0.2: {} - ora@5.3.0: dependencies: bl: 4.1.0 @@ -18142,7 +18104,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) get-uri: 6.0.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 @@ -18523,7 +18485,7 @@ snapshots: proxy-agent@6.3.1: dependencies: agent-base: 7.1.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 lru-cache: 7.18.3 @@ -19312,7 +19274,7 @@ snapshots: socket.io-adapter@2.5.5: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) ws: 8.17.1 transitivePeerDependencies: - bufferutil @@ -19333,7 +19295,7 @@ snapshots: socket.io-parser@4.2.4: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -19342,7 +19304,7 @@ snapshots: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) engine.io: 6.5.5 socket.io-adapter: 2.5.5 socket.io-parser: 4.2.4 @@ -19363,7 +19325,7 @@ snapshots: socks-proxy-agent@8.0.4: dependencies: agent-base: 7.1.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -19409,7 +19371,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -19420,7 +19382,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -19514,7 +19476,7 @@ snapshots: streamroller@3.1.5: dependencies: date-format: 4.0.14 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) fs-extra: 8.1.0 transitivePeerDependencies: - supports-color @@ -19621,7 +19583,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 3.5.1 @@ -19848,7 +19810,7 @@ snapshots: tuf-js@2.2.1: dependencies: '@tufjs/models': 2.0.1 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) make-fetch-happen: 13.0.1 transitivePeerDependencies: - supports-color @@ -20173,7 +20135,7 @@ snapshots: wait-on@8.0.1: dependencies: - axios: 1.7.7 + axios: 1.7.7(debug@4.3.7) joi: 17.13.3 lodash: 4.17.21 minimist: 1.2.8 @@ -20185,7 +20147,7 @@ snapshots: dependencies: chalk: 4.1.2 commander: 9.5.0 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.6(supports-color@5.5.0) transitivePeerDependencies: - supports-color From 9aaaa8882b41fd3af2eb42b5b7525b9d8581a9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:16:57 +0100 Subject: [PATCH 2/3] chore: add watchPaths --- .../ui5-middleware-livereload/lib/livereload.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/ui5-middleware-livereload/lib/livereload.js b/packages/ui5-middleware-livereload/lib/livereload.js index 56c26293..d0585983 100644 --- a/packages/ui5-middleware-livereload/lib/livereload.js +++ b/packages/ui5-middleware-livereload/lib/livereload.js @@ -128,20 +128,20 @@ module.exports = async ({ log, resources, options, middlewareUtil }) => { let debug = options?.configuration?.debug; let usePolling = options?.configuration?.usePolling; + // Ensure watchPath is always an array + const watchPaths = Array.isArray(watchPath) ? watchPath : [watchPath]; + + // Create complete paths with extensions + const pathsToWatch = watchPaths.flatMap((basePath) => extraExts.split(",").map((ext) => path.join(basePath, `**/*.${ext.trim()}`))); + // Set up WebSocket server const wss = new WebSocket.Server({ port }); wss.on("connection", (ws) => { debug && log.info("WebSocket client connected"); }); - // Build array of file extensions to watch - const extsToWatch = extraExts.split(","); - - // Prepare glob patterns for chokidar - const globPatterns = extsToWatch.map((ext) => `**/*.${ext}`); - // Set up chokidar watcher - const watcher = chokidar.watch(watchPath, { + const watcher = chokidar.watch(pathsToWatch, { ignored: exclusions, ignoreInitial: true, usePolling: usePolling, From 353e69ccc9d2c9f0e14f976cda586d42f15989d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:31:52 +0100 Subject: [PATCH 3/3] fix: add https support --- .../lib/livereload.js | 97 +++++++++++++------ 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/packages/ui5-middleware-livereload/lib/livereload.js b/packages/ui5-middleware-livereload/lib/livereload.js index d0585983..c4bfd2b4 100644 --- a/packages/ui5-middleware-livereload/lib/livereload.js +++ b/packages/ui5-middleware-livereload/lib/livereload.js @@ -1,10 +1,12 @@ /* eslint-disable no-unused-vars, no-undef */ 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 @@ -13,6 +15,7 @@ const WebSocket = require("ws"); * @property {string|yo} [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} [debug] see output + * @property {boolean} [usePolling] - enable polling for file changes */ /** @@ -21,7 +24,7 @@ const WebSocket = require("ws"); * * @param {object} options the entered config option * @param {number} defaultPort the port which is defaulted - * @returns {number} a port which is free + * @returns {Promise} - a port which is free */ const getPortForLivereload = async (options, defaultPort) => { if (options.configuration && options.configuration.port) { @@ -41,6 +44,7 @@ const getPortForLivereload = async (options, defaultPort) => { * ATTENTION: this is a hack to be compatible with UI5 tooling 2.x and 3.x * * @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) => { @@ -124,24 +128,35 @@ module.exports = async ({ log, resources, options, middlewareUtil }) => { } else if (exclusions) { exclusions = [new RegExp(exclusions)]; } - let extraExts = options?.configuration?.extraExts || "js,html,css,jsx,ts,tsx,xml,json,properties"; - let debug = options?.configuration?.debug; - let usePolling = options?.configuration?.usePolling; - - // Ensure watchPath is always an array - const watchPaths = Array.isArray(watchPath) ? watchPath : [watchPath]; + const extraExts = options?.configuration?.extraExts || "js,html,css,jsx,ts,tsx,xml,json,properties"; + const debug = options?.configuration?.debug; + const usePolling = options?.configuration?.usePolling; + + // SSL Configuration via command-line arguments + const cli = yargs(process.argv.slice(2)).argv; + + 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."); + } + } - // Create complete paths with extensions - const pathsToWatch = watchPaths.flatMap((basePath) => extraExts.split(",").map((ext) => path.join(basePath, `**/*.${ext.trim()}`))); - - // Set up WebSocket server - const wss = new WebSocket.Server({ port }); - wss.on("connection", (ws) => { - debug && log.info("WebSocket client connected"); - }); + // Build array of file extensions to watch + const extsToWatch = extraExts.split(","); // Set up chokidar watcher - const watcher = chokidar.watch(pathsToWatch, { + const watcher = chokidar.watch(watchPath, { ignored: exclusions, ignoreInitial: true, usePolling: usePolling, @@ -150,14 +165,37 @@ module.exports = async ({ log, resources, options, middlewareUtil }) => { depth: Infinity, }); + // Set up WebSocket server + let server; + if (useHttps && Object.keys(serverOptions).length > 0) { + // HTTPS Server + server = require("https").createServer(serverOptions); + } else { + // HTTP Server + server = require("http").createServer(); + } + server.listen(port, () => { + debug && log.info(`Livereload server started on port ${port}`); + }); + + const wss = new WebSocket.Server({ server }); + + wss.on("connection", (ws) => { + debug && log.info("WebSocket client connected"); + }); + watcher.on("all", (event, filePath) => { - debug && log.info(`File ${event}: ${filePath}`); - // Notify all connected clients to reload - wss.clients.forEach((client) => { - if (client.readyState === WebSocket.OPEN) { - client.send("reload"); - } - }); + // 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 @@ -174,21 +212,26 @@ module.exports = async ({ log, resources, options, middlewareUtil }) => { let body = ""; res.write = function (chunk) { - body += chunk.toString(); + body += chunk instanceof Buffer ? chunk.toString() : chunk; }; res.end = function (chunk) { if (chunk) { - body += chunk.toString(); + 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 = `