diff --git a/dev/build/esbuild.js b/dev/build/esbuild.js index 63387d612..cb9abc246 100644 --- a/dev/build/esbuild.js +++ b/dev/build/esbuild.js @@ -1,12 +1,16 @@ #!/usr/bin/env node -const esbuild = require('esbuild'); -const path = require('path'); -const fs = require('fs'); +import * as esbuild from 'esbuild'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import * as process from "node:process"; + // Check if we're building for production // (Set via passing `production` as first argument) -const isProd = process.argv[2] === 'production'; +const mode = process.argv[2]; +const isProd = mode === 'production'; +const __dirname = import.meta.dirname; // Gather our input files const entryPoints = { @@ -17,11 +21,16 @@ const entryPoints = { wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'), }; +// Watch styles so we can reload on change +if (mode === 'watch') { + entryPoints['styles-dummy'] = path.join(__dirname, '../../public/dist/styles.css'); +} + // Locate our output directory const outdir = path.join(__dirname, '../../public/dist'); -// Build via esbuild -esbuild.build({ +// Define the options for esbuild +const options = { bundle: true, metafile: true, entryPoints, @@ -33,6 +42,7 @@ esbuild.build({ minify: isProd, logLevel: 'info', loader: { + '.html': 'copy', '.svg': 'text', }, absWorkingDir: path.join(__dirname, '../..'), @@ -45,6 +55,28 @@ esbuild.build({ js: '// See the "/licenses" URI for full package license details', css: '/* See the "/licenses" URI for full package license details */', }, -}).then(result => { - fs.writeFileSync('esbuild-meta.json', JSON.stringify(result.metafile)); -}).catch(() => process.exit(1)); +}; + +if (mode === 'watch') { + options.inject = [ + path.join(__dirname, './livereload.js'), + ]; +} + +const ctx = await esbuild.context(options); + +if (mode === 'watch') { + // Watch for changes and rebuild on change + ctx.watch({}); + let {hosts, port} = await ctx.serve({ + servedir: path.join(__dirname, '../../public'), + cors: { + origin: '*', + } + }); +} else { + // Build with meta output for analysis + ctx.rebuild().then(result => { + fs.writeFileSync('esbuild-meta.json', JSON.stringify(result.metafile)); + }).catch(() => process.exit(1)); +} diff --git a/dev/build/livereload.js b/dev/build/livereload.js new file mode 100644 index 000000000..b4bf38e6d --- /dev/null +++ b/dev/build/livereload.js @@ -0,0 +1,35 @@ +if (!window.__dev_reload_listening) { + listen(); + window.__dev_reload_listening = true; +} + + +function listen() { + console.log('Listening for livereload events...'); + new EventSource("http://127.0.0.1:8000/esbuild").addEventListener('change', e => { + const { added, removed, updated } = JSON.parse(e.data); + + if (!added.length && !removed.length && updated.length > 0) { + const updatedPath = updated.filter(path => path.endsWith('.css'))[0] + if (!updatedPath) return; + + const links = [...document.querySelectorAll("link[rel='stylesheet']")]; + for (const link of links) { + const url = new URL(link.href); + const name = updatedPath.replace('-dummy', ''); + + if (url.pathname.endsWith(name)) { + const next = link.cloneNode(); + next.href = name + '?' + Math.random().toString(36).slice(2); + next.onload = function() { + link.remove(); + }; + link.after(next); + return + } + } + } + + location.reload() + }); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b85d1f5e2..e8a1493d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "eslint-plugin-import": "^2.32.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", - "livereload": "^0.10.3", "npm-run-all": "^4.1.5", "sass": "^1.94.2", "ts-jest": "^29.4.5", @@ -7027,62 +7026,6 @@ "uc.micro": "^2.0.0" } }, - "node_modules/livereload": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.10.3.tgz", - "integrity": "sha512-llSb8HrtSH7ByPFMc8WTTeW3oy++smwgSA8JVGzEn8KiDPESq6jt1M4ZKKkhKTrhn2wvUOadQq4ip10E5daZ3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.3", - "livereload-js": "^4.0.2", - "opts": "^2.0.2", - "ws": "^8.4.3" - }, - "bin": { - "livereload": "bin/livereload.js" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/livereload-js": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-4.0.2.tgz", - "integrity": "sha512-Fy7VwgQNiOkynYyNBTo3v9hQUhcW5pFAheJN148+DTgpShjsy/22pLHKKwDK5v0kOsZsJBK+6q1PMgLvRmrwFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/livereload/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/livereload/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -7771,13 +7714,6 @@ "node": ">= 0.8.0" } }, - "node_modules/opts": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", - "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", diff --git a/package.json b/package.json index 964e3ee7e..1ae14e976 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "private": true, + "type": "module", "scripts": { "build:css:dev": "sass ./resources/sass:./public/dist --embed-sources", "build:css:watch": "sass ./resources/sass:./public/dist --watch --embed-sources", "build:css:production": "sass ./resources/sass:./public/dist -s compressed", "build:js:dev": "node dev/build/esbuild.js", - "build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" \"./resources/**/*.ts\" -c \"npm run build:js:dev\"", + "build:js:watch": "node dev/build/esbuild.js watch", "build:js:production": "node dev/build/esbuild.js production", "build": "npm-run-all --parallel build:*:dev", "production": "npm-run-all --parallel build:*:production", - "dev": "npm-run-all --parallel watch livereload", + "dev": "npm-run-all --parallel build:*:watch", "watch": "npm-run-all --parallel build:*:watch", - "livereload": "livereload ./public/dist/", "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads", "lint": "eslint \"resources/**/*.js\" \"resources/**/*.mjs\"", "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\"", @@ -29,7 +29,6 @@ "eslint-plugin-import": "^2.32.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", - "livereload": "^0.10.3", "npm-run-all": "^4.1.5", "sass": "^1.94.2", "ts-jest": "^29.4.5",