diff --git a/develop/Makefile b/develop/Makefile index 4fda8d80cb..8991236c26 100644 --- a/develop/Makefile +++ b/develop/Makefile @@ -19,6 +19,23 @@ endif HOST_LAN_IP ?= export HOST_LAN_IP +# --- Front hot-reload (source mode) --- +# All paths below are INSIDE the eo container. The repo root is bind-mounted at /develop +# (docker-compose.yml: `../:/develop`), which is why source paths are /develop/develop/... . +EO_REPO := /develop +LIVERELOAD_DIR := $(EO_REPO)/develop/setup/livereload-tools +LIVERELOAD_SRV := $(LIVERELOAD_DIR)/server.js +LIVERELOAD_PAT := $(LIVERELOAD_DIR)/[s]erver.js +NGINX_CONF := /etc/nginx/includes/ds-docservice.conf +NGINX_CONF_BAK := $(NGINX_CONF).prod.bak +DEV_CONF_SRC := $(EO_REPO)/develop/setup/ds-docservice-dev.conf +SDK_SENTINEL := $(EO_REPO)/sdkjs/common/AllFonts.js +# Canonical published docs (from documentation/brand.yml: brand.docs_url). +DOCS_HOTRELOAD := https://docs.euro-office.com/development/hot-reload/ + +.PHONY: local mobile next next-mobile pull wipe-nc wipe-next build \ + front-dev front-prod front-dev-live sdkjs-dev refresh-urls print-nc-version + local: @echo "" @echo " ███████╗ ██████╗" @@ -112,6 +129,77 @@ build: (docker compose up -d) && \ (docker compose exec eo bash || true) +# Front hot-reload: serves /web-apps from the SOURCE tree (/develop) with no cache, so that +# editing + reloading the browser (Ctrl+Shift+R) reflects the change WITHOUT a rebuild. +# The SDK is also served from source via its develop loader (run `make sdkjs-dev` once; +# front-dev aborts if it is missing). Reversible with `make front-prod`. +# Details and limitations: https://docs.euro-office.com/development/hot-reload/ +front-dev: + @docker compose exec -u root eo bash -lc '\ + if [ ! -f $(SDK_SENTINEL) ] || [ ! -d $(EO_REPO)/sdkjs/develop/sdkjs ]; then \ + echo "ERROR: SDK develop loader missing — run: make sdkjs-dev"; \ + echo " Docs: $(DOCS_HOTRELOAD)"; \ + exit 1; \ + fi; \ + [ -f $(NGINX_CONF_BAK) ] || cp $(NGINX_CONF) $(NGINX_CONF_BAK); \ + cp $(DEV_CONF_SRC) $(NGINX_CONF); \ + if nginx_err=$$(nginx -t 2>&1); then \ + nginx -s reload && sleep 3; \ + else \ + echo "ERROR: dev nginx config is invalid — rolling back to production config."; \ + echo "$$nginx_err"; \ + cp $(NGINX_CONF_BAK) $(NGINX_CONF) && nginx -s reload 2>/dev/null || true; \ + exit 1; \ + fi' + @echo "" + @echo " SOURCE mode active (front hot-reload)." + @echo " Edit under web-apps/apps/.../ and hard-reload the browser (Ctrl+Shift+R)." + @echo " Back to production: make front-prod" + @echo "" + +front-prod: + @docker compose exec -u root eo bash -lc "pkill -f '$(LIVERELOAD_PAT)' 2>/dev/null; true" + @docker compose exec -u root eo bash -lc '\ + if [ -f $(NGINX_CONF_BAK) ]; then \ + cp $(NGINX_CONF_BAK) $(NGINX_CONF); \ + nginx -t && nginx -s reload && sleep 3; \ + echo "PRODUCTION mode restored (compiled bundle + versioned cache)."; \ + else echo "No backup found; the container is already in production mode."; fi' + +# Like front-dev, but also starts the live-reload server: saving a file under web-apps/ +# auto-reloads the editor tab (no browser action). The livereload client is injected via +# nginx (sub_filter) and proxied through :8080. Node deps install automatically on first +# run (npm ci, idempotent). Details: https://docs.euro-office.com/development/hot-reload/ +front-dev-live: front-dev + @docker compose exec -u root eo bash -lc "pkill -f '$(LIVERELOAD_PAT)' 2>/dev/null; true" + @docker compose exec eo bash -lc '\ + if [ ! -d $(LIVERELOAD_DIR)/node_modules ]; then \ + echo "Installing live-reload dependencies (one-time)..."; \ + cd $(LIVERELOAD_DIR) && npm ci --no-audit --no-fund; \ + fi' + @docker compose exec -d eo bash -lc 'node $(LIVERELOAD_SRV) > /tmp/livereload.log 2>&1' + @sleep 2 + @docker compose exec eo bash -lc 'curl -sf http://127.0.0.1:35729/livereload.js >/dev/null' \ + && echo "Live-reload server running (:35729, polling)." \ + || { echo "ERROR: live-reload server did not start. Log follows:"; \ + docker compose exec eo cat /tmp/livereload.log; \ + exit 1; } + @echo "" + @echo " Auto live-reload ACTIVE. Edit under web-apps/ and save:" + @echo " the editor tab reloads on its own (no commands)." + @echo " Stop: make front-prod" + @echo " Note: if you restart the eo container, re-run this command to restore live-reload." + @echo "" + +# One-time SDK setup for source mode. Generates the SDK "develop loader" +# (sdkjs/develop/sdkjs/*/scripts.js) in the SOURCE tree and places the generated +# AllFonts.js there, so /develop/sdkjs serves a complete SDK coherent with the front. +# Re-run only when you change the sdkjs submodule. Then use `make front-dev-live`. +sdkjs-dev: + @docker compose exec eo bash -lc 'cd $(EO_REPO)/sdkjs/build && npm install --no-audit --no-fund && grunt develop' + @docker compose exec eo bash -lc 'cp $$EO_ROOT/sdkjs/common/AllFonts.js $(SDK_SENTINEL)' + @echo "SDK develop loader generated under $(EO_REPO)/sdkjs. Next: make front-dev-live" + # Align Euro-Office URLs and trusted_domains with the current mode. # - If HOST_LAN_IP is set: mobile/LAN mode (DocumentServerUrl = http://:8080/, trusted_domains[2] = ). # - If HOST_LAN_IP is empty: localhost mode (DocumentServerUrl = http://localhost:8080/, trusted_domains[2] cleared). diff --git a/develop/setup/ds-docservice-dev.conf b/develop/setup/ds-docservice-dev.conf new file mode 100644 index 0000000000..8a7de2feca --- /dev/null +++ b/develop/setup/ds-docservice-dev.conf @@ -0,0 +1,153 @@ +# ds-docservice-dev.conf — DEVELOPMENT: editor served 100% from SOURCE (/develop) +# +# Replicates ONLYOFFICE's official "developer mode" (build_tools/develop): api.js, web-apps and +# sdkjs are served from the SOURCE tree, mutually coherent, instead of mixing compiled pieces +# (the root cause of the "Asc is not defined" and "R_ null" errors). +# +# Key points: +# - The SOURCE api.js keeps the {{PRODUCT_VERSION}} placeholder -> DocsAPI (extendAppPath, +# api.js:1327) does NOT add the /X.Y.Z-/ version prefix -> the editor loads from +# /web-apps/... unversioned and the SDK load order (synchronous document.write -> RequireJS) +# works as ONLYOFFICE designed it. +# - SDK served from /develop/sdkjs with the real develop loader (grunt develop) + AllFonts.js -> +# coherent with the front. Falls back to $EO_ROOT for generated binaries (.wasm, theme.bin) +# that are not present in the source tree. +# - Service worker neutralised + auto live-reload (livereload proxied through :8080). +# +# One-time requirement: the SDK develop loader in the source tree (make sdkjs-dev) and AllFonts.js +# under /develop/sdkjs/common. Toggle with: make front-dev / front-dev-live / front-prod. +# Docs: https://docs.euro-office.com/development/hot-reload/ +# +# MAINTENANCE: the access-controlled tail of this file (cache/files secure_link, internal, +# info, versioned passthrough) mirrors the production ds-docservice.conf shipped in the image. +# If that production config changes (e.g. an access-control fix), mirror it in the blocks below. + +#welcome page +location = / { return 302 $the_scheme://$the_host$the_prefix/welcome/; } + +# --- DEV: front (web-apps) + api.js from SOURCE, no cache --- +location ^~ /web-apps/ { + alias /develop/web-apps/; + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + expires off; + error_log /dev/null crit; # silence unsupported-locale noise + gzip off; + sub_filter_once off; + # auto live-reload: inject the client (proxied through :8080, see /livereload* locations) + sub_filter '' ''; + try_files $uri =404; +} + +# --- DEV: SDK from SOURCE (develop loader + individual sources coherent with the front) --- +# Falls back to $EO_ROOT (@sdkjs_compiled) for generated binaries (.wasm, etc.) absent in source. +location ^~ /sdkjs/ { + alias /develop/sdkjs/; + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + expires off; + error_log /dev/null crit; + try_files $uri @sdkjs_compiled; +} +location @sdkjs_compiled { + root /var/www/euro-office/documentserver; + add_header Cache-Control "no-store" always; +} + +# --- Auto live-reload: proxy the client and the websocket to the internal :35729 server --- +# The client script is injected into every page (sub_filter above). In plain `front-dev` +# mode (no live server running) the upstream is down; we intercept the error and serve a +# harmless no-op so the browser console stays clean instead of logging a failed script load. +location = /livereload.js { + proxy_pass http://127.0.0.1:35729; + proxy_intercept_errors on; + error_page 502 504 = @livereload_off; +} +location @livereload_off { + default_type application/javascript; + add_header Cache-Control "no-store" always; + return 200 "/* live-reload server not running - start it with: make front-dev-live */"; +} +location /livereload { + proxy_pass http://127.0.0.1:35729; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400; +} + +# --- DEV: service worker NEUTRALISED. The production SW caches the editor and breaks +# hot-reload (responses come back "from service worker"); we serve a SW that, on activation, +# DELETES all caches and does not intercept fetch. +location ~ /document_editor_service_worker\.js$ { + default_type application/javascript; + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + return 200 "self.addEventListener('install',function(){self.skipWaiting()});self.addEventListener('activate',function(e){e.waitUntil(caches.keys().then(function(k){return Promise.all(k.map(function(c){return caches.delete(c);}));}).then(function(){return self.clients.claim();}));});"; +} + +# fonts / dictionaries / sdkjs-plugins → compiled ($EO_ROOT); unchanged while editing UI +location ~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\w]+)?\/(sdkjs-plugins|fonts|dictionaries)(\/.*)$ { + add_header Cache-Control "no-store" always; + error_log /dev/null crit; + alias /var/www/euro-office/documentserver/$2$3; +} + +location ~* ^(\/cache\/files.*)(\/.*) { + alias /var/lib/euro-office/documentserver/App_Data$1; + add_header Content-Disposition "attachment; filename*=UTF-8''$arg_filename"; + + secure_link $arg_md5,$arg_expires; + secure_link_md5 "$secure_link_expires$uri$secure_link_secret"; + + if ($secure_link = "") { + return 403; + } + + if ($secure_link = "0") { + return 410; + } +} + +# Allow "/internal" interface only from 127.0.0.1 +location ~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\w]+)?\/(internal)(\/.*)$ { + allow 127.0.0.1; + deny all; + proxy_pass http://docservice/$2$3; +} + +# Allow "/info" interface only from 127.0.0.1 by default +location ~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\w]+)?\/(info)(\/.*)$ { + allow 127.0.0.1; + deny all; + proxy_pass http://docservice/$2$3; +} + +#document formats discovery +location ~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\w]+)?\/(meta\/formats)$ { + add_header Cache-Control "no-store" always; + alias /var/www/euro-office/documentserver/document-formats/onlyoffice-docs-formats.json; +} + +location / { + proxy_pass http://docservice; + proxy_http_version 1.1; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_connect_timeout 300s; + proxy_buffering on; + proxy_buffers 64 32k; + proxy_busy_buffers_size 64k; + proxy_max_temp_file_size 0; + proxy_redirect off; +} + +location ~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\w]+)?\/(ai-proxy)(\/.*)?$ { + proxy_pass http://docservice/$2$3; + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + send_timeout 300s; +} + +location ~ ^/([\d]+\.[\d]+\.[\d]+[\.|-][\w]+)/(?.*)$ { + proxy_pass http://docservice/$path$is_args$args; + proxy_http_version 1.1; +} diff --git a/develop/setup/livereload-tools/.gitignore b/develop/setup/livereload-tools/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/develop/setup/livereload-tools/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/develop/setup/livereload-tools/package-lock.json b/develop/setup/livereload-tools/package-lock.json new file mode 100644 index 0000000000..9309d944ff --- /dev/null +++ b/develop/setup/livereload-tools/package-lock.json @@ -0,0 +1,252 @@ +{ + "name": "eo-livereload", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eo-livereload", + "version": "1.0.0", + "dependencies": { + "livereload": "^0.9.3" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "license": "BSD-2-Clause" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ws": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/develop/setup/livereload-tools/package.json b/develop/setup/livereload-tools/package.json new file mode 100644 index 0000000000..2f1b6380a7 --- /dev/null +++ b/develop/setup/livereload-tools/package.json @@ -0,0 +1,7 @@ +{ + "name": "eo-livereload", + "private": true, + "version": "1.0.0", + "description": "Live-reload server for front hot-reload (source mode)", + "dependencies": { "livereload": "^0.9.3" } +} diff --git a/develop/setup/livereload-tools/server.js b/develop/setup/livereload-tools/server.js new file mode 100644 index 0000000000..e9880b1449 --- /dev/null +++ b/develop/setup/livereload-tools/server.js @@ -0,0 +1,38 @@ +// Live-reload server for front hot-reload (source mode). +// Watches the web-apps sources and tells the browser to reload on save. +// Uses POLLING: inotify events do not cross the Windows<->WSL boundary reliably, +// so we detect changes by polling mtimes. +const livereload = require('livereload'); + +// Polling cost: chokidar in polling mode stat()s every watched file on each tick, and +// web-apps/apps holds ~20k files. So we (a) poll at 1s rather than 500ms — halving the +// stat load on the WSL/9p mount — and (b) exclude heavy trees that are never hand-edited +// during UI iteration (third-party vendor bundles, minified files, source maps, build +// output). "save -> reload" stays ~1-2s. Lower `interval` only if you have spare CPU. +const server = livereload.createServer({ + port: 35729, + delay: 200, + // chokidar in polling mode (reliable for files edited from Windows/\\wsl.localhost) + usePolling: true, + interval: 1000, + binaryInterval: 2000, + exts: ['js', 'template', 'less', 'css', 'json', 'html', 'svg'], + exclusions: [/node_modules/, /\.git/, /build\/deploy/, /\/vendor\//, /\.min\.js$/, /\.map$/], +}); + +server.watch('/develop/web-apps/apps'); +console.log('[livereload] watching /develop/web-apps/apps (polling 1000ms) on :35729'); + +// Optional: auto-reload on sdkjs (document engine) changes. +// Works reliably on Linux native. On WSL/Windows the polling cross-boundary +// (\\wsl.localhost\ → ext4) is unreliable — changes may not be detected there, +// so use Ctrl+Shift+R manually in that case. +// After uncommenting, restart the server: make front-prod && make front-dev-live +// NOTE: if your change ADDS or REMOVES SDK files, re-run `make sdkjs-dev` regardless. +// server.watch('/develop/sdkjs'); +// console.log('[livereload] also watching /develop/sdkjs'); + +// Visible log of every detected change (confirms polling works). +if (server.watcher && server.watcher.on) { + server.watcher.on('change', function (f) { console.log('[livereload] change:', f); }); +}