From 37dda671cfc970166057fd888d2acdfd829feb24 Mon Sep 17 00:00:00 2001 From: vm-silicons Date: Sat, 20 Dec 2025 05:35:26 -0800 Subject: [PATCH] sync dev server --- tgui/packages/tgui-dev-server/dreamseeker.js | 7 +- tgui/packages/tgui-dev-server/index.js | 10 +- tgui/packages/tgui-dev-server/link/client.cjs | 177 ------------------ tgui/packages/tgui-dev-server/link/client.mjs | 169 +++++++++++++++++ tgui/packages/tgui-dev-server/link/retrace.js | 37 ++-- tgui/packages/tgui-dev-server/link/server.js | 85 ++++----- tgui/packages/tgui-dev-server/logging.js | 40 ++-- tgui/packages/tgui-dev-server/package.json | 25 +-- tgui/packages/tgui-dev-server/reloader.js | 58 +++--- tgui/packages/tgui-dev-server/require.js | 2 +- tgui/packages/tgui-dev-server/util.js | 27 ++- tgui/packages/tgui-dev-server/webpack.js | 49 +++-- tgui/packages/tgui-dev-server/winreg.js | 39 ++-- 13 files changed, 364 insertions(+), 361 deletions(-) delete mode 100644 tgui/packages/tgui-dev-server/link/client.cjs create mode 100644 tgui/packages/tgui-dev-server/link/client.mjs diff --git a/tgui/packages/tgui-dev-server/dreamseeker.js b/tgui/packages/tgui-dev-server/dreamseeker.js index a85801986e..e8910d8c3b 100644 --- a/tgui/packages/tgui-dev-server/dreamseeker.js +++ b/tgui/packages/tgui-dev-server/dreamseeker.js @@ -6,9 +6,8 @@ import { exec } from 'node:child_process'; import { promisify } from 'node:util'; -import { require } from './require.js'; -const axios = require('axios'); +import axios from 'axios'; import { createLogger } from './logging.js'; @@ -119,6 +118,6 @@ export class DreamSeeker { } } -const plural = (word, n) => { +function plural(word, n) { return n !== 1 ? word + 's' : word; -}; +} diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.js index 199e93d836..b51162d6e0 100644 --- a/tgui/packages/tgui-dev-server/index.js +++ b/tgui/packages/tgui-dev-server/index.js @@ -4,27 +4,27 @@ * @license MIT */ -import { createCompiler } from './webpack.js'; import { reloadByondCache } from './reloader.js'; +import { createCompiler } from './webpack.js'; const noHot = process.argv.includes('--no-hot'); const noTmp = process.argv.includes('--no-tmp'); const reloadOnce = process.argv.includes('--reload'); -const setupServer = async () => { +async function setupServer() { const compiler = await createCompiler({ - mode: 'development', hot: !noHot, - devServer: true, useTmpFolder: !noTmp, }); + // Reload cache once if (reloadOnce) { await reloadByondCache(compiler.bundleDir); return; } + // Run a development server await compiler.watch(); -}; +} setupServer(); diff --git a/tgui/packages/tgui-dev-server/link/client.cjs b/tgui/packages/tgui-dev-server/link/client.cjs deleted file mode 100644 index fe75314acd..0000000000 --- a/tgui/packages/tgui-dev-server/link/client.cjs +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -let socket; -const queue = []; -const subscribers = []; - -const ensureConnection = () => { - if (process.env.NODE_ENV !== 'production') { - if (!window.WebSocket) { - return; - } - if (!socket || socket.readyState === WebSocket.CLOSED) { - const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; - socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`); - socket.onopen = () => { - // Empty the message queue - while (queue.length !== 0) { - const msg = queue.shift(); - socket.send(msg); - } - }; - socket.onmessage = event => { - const msg = JSON.parse(event.data); - for (let subscriber of subscribers) { - subscriber(msg); - } - }; - } - } -}; - -if (process.env.NODE_ENV !== 'production') { - window.onunload = () => socket && socket.close(); -} - -const subscribe = fn => subscribers.push(fn); - -/** - * A json serializer which handles circular references and other junk. - */ -const serializeObject = obj => { - let refs = []; - const primitiveReviver = value => { - if (typeof value === 'number' && !Number.isFinite(value)) { - return { - __number__: String(value), - }; - } - if (typeof value === 'undefined') { - return { - __undefined__: true, - }; - } - return value; - }; - const objectReviver = (key, value) => { - if (typeof value === 'object') { - if (value === null) { - return value; - } - // Circular reference - if (refs.includes(value)) { - return '[circular ref]'; - } - refs.push(value); - // Error object - const isError = value instanceof Error || ( - value.code && value.message && value.message.includes('Error') - ); - if (isError) { - return { - __error__: true, - string: String(value), - stack: value.stack, - }; - } - // Array - if (Array.isArray(value)) { - return value.map(primitiveReviver); - } - return value; - } - return primitiveReviver(value); - }; - const json = JSON.stringify(obj, objectReviver); - refs = null; - return json; -}; - -const sendMessage = msg => { - if (process.env.NODE_ENV !== 'production') { - const json = serializeObject(msg); - // Send message using WebSocket - if (window.WebSocket) { - ensureConnection(); - if (socket.readyState === WebSocket.OPEN) { - socket.send(json); - } - else { - // Keep only 100 latest messages in the queue - if (queue.length > 100) { - queue.shift(); - } - queue.push(json); - } - } - // Send message using plain HTTP request. - else { - const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; - const req = new XMLHttpRequest(); - req.open('POST', `http://${DEV_SERVER_IP}:3001`, true); - req.timeout = 250; - req.send(json); - } - } -}; - -const sendLogEntry = (level, ns, ...args) => { - if (process.env.NODE_ENV !== 'production') { - try { - sendMessage({ - type: 'log', - payload: { - level, - ns: ns || 'client', - args, - }, - }); - } - catch (err) {} - } -}; - -const setupHotReloading = () => { - if (process.env.NODE_ENV !== 'production' - && process.env.WEBPACK_HMR_ENABLED - && window.WebSocket) { - if (module.hot) { - ensureConnection(); - sendLogEntry(0, null, 'setting up hot reloading'); - subscribe(msg => { - const { type } = msg; - sendLogEntry(0, null, 'received', type); - if (type === 'hotUpdate') { - const status = module.hot.status(); - if (status !== 'idle') { - sendLogEntry(0, null, 'hot reload status:', status); - return; - } - module.hot - .check({ - ignoreUnaccepted: true, - ignoreDeclined: true, - ignoreErrored: true, - }) - .then(modules => { - sendLogEntry(0, null, 'outdated modules', modules); - }) - .catch(err => { - sendLogEntry(0, null, 'reload error', err); - }); - } - }); - } - } -}; - -module.exports = { - subscribe, - sendMessage, - sendLogEntry, - setupHotReloading, -}; diff --git a/tgui/packages/tgui-dev-server/link/client.mjs b/tgui/packages/tgui-dev-server/link/client.mjs new file mode 100644 index 0000000000..350f0484bd --- /dev/null +++ b/tgui/packages/tgui-dev-server/link/client.mjs @@ -0,0 +1,169 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +let socket; +const queue = []; +const subscribers = []; + +function ensureConnection() { + if (process.env.NODE_ENV === 'production') return; + + if (socket && socket.readyState !== WebSocket.CLOSED) return; + + if (!window.WebSocket) return; + + const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; + + socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`); + + socket.onopen = () => { + // Empty the message queue + while (queue.length !== 0) { + const msg = queue.shift(); + socket.send(msg); + } + }; + + socket.onmessage = (event) => { + const msg = JSON.parse(event.data); + for (let subscriber of subscribers) { + subscriber(msg); + } + }; + + window.onunload = () => socket?.close(); +} + +export function subscribe(fn) { + subscribers.push(fn); +} + +function primitiveReviver(value) { + if (typeof value === 'number' && !Number.isFinite(value)) { + return { + __number__: String(value), + }; + } + if (typeof value === 'undefined') { + return { + __undefined__: true, + }; + } + return value; +} + +/** + * A json serializer which handles circular references and other junk. + */ +function serializeObject(obj) { + let refs = []; + + function objectReviver(key, value) { + if (typeof value !== 'object') { + return primitiveReviver(value); + } + + if (value === null) { + return value; + } + // Circular reference + if (refs.includes(value)) { + return '[circular ref]'; + } + refs.push(value); + // Error object + const isError = + value instanceof Error || + (value.code && value.message && value.message.includes('Error')); + if (isError) { + return { + __error__: true, + string: String(value), + stack: value.stack, + }; + } + // Array + if (Array.isArray(value)) { + return value.map(primitiveReviver); + } + return value; + } + + const json = JSON.stringify(obj, objectReviver); + refs = null; + return json; +} + +export function sendMessage(msg) { + if (process.env.NODE_ENV === 'production') return; + + const json = serializeObject(msg); + // Send message using WebSocket + if (!window.WebSocket) return; + + ensureConnection(); + if (socket.readyState === WebSocket.OPEN) { + socket.send(json); + } else { + // Keep only 100 latest messages in the queue + if (queue.length > 100) { + queue.shift(); + } + queue.push(json); + } +} + +export function sendLogEntry(level, ns, ...args) { + if (process.env.NODE_ENV !== 'production') { + try { + sendMessage({ + type: 'log', + payload: { + level, + ns: ns || 'client', + args, + }, + }); + } catch (err) {} + } +} + +export function setupHotReloading() { + if ( + process.env.NODE_ENV === 'production' || + !process.env.WEBPACK_HMR_ENABLED || + !window.WebSocket + ) { + return; + } + if (!import.meta.webpackHot) return; + + ensureConnection(); + sendLogEntry(0, null, 'setting up hot reloading'); + subscribe(({ type }) => { + sendLogEntry(0, null, 'received', type); + if (type !== 'hotUpdate') return; + + const status = import.meta.webpackHot.status(); + if (status !== 'idle') { + sendLogEntry(0, null, 'hot reload status:', status); + return; + } + + import.meta.webpackHot + .check({ + ignoreUnaccepted: true, + ignoreDeclined: true, + ignoreErrored: true, + }) + .then((modules) => { + sendLogEntry(0, null, 'outdated modules', modules); + }) + .catch((err) => { + sendLogEntry(0, null, 'reload error', err); + }); + }); +} diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.js index c10ba9cb17..6aedb88ef7 100644 --- a/tgui/packages/tgui-dev-server/link/retrace.js +++ b/tgui/packages/tgui-dev-server/link/retrace.js @@ -4,21 +4,20 @@ * @license MIT */ -import fs from 'fs'; -import { basename } from 'path'; -import { createLogger } from '../logging.js'; -import { require } from '../require.js'; -import { resolveGlob } from '../util.js'; +import fs from 'node:fs'; +import { basename } from 'node:path'; -const SourceMap = require('source-map'); -const { parse: parseStackTrace } = require('stacktrace-parser'); +import { SourceMapConsumer } from 'source-map'; +import { parse as parseStackTrace } from 'stacktrace-parser'; + +import { createLogger } from '../logging.js'; +import { resolveGlob } from '../util.js'; const logger = createLogger('retrace'); -const { SourceMapConsumer } = SourceMap; const sourceMaps = []; -export const loadSourceMaps = async bundleDir => { +export async function loadSourceMaps(bundleDir) { // Destroy and garbage collect consumers while (sourceMaps.length !== 0) { const { consumer } = sourceMaps.shift(); @@ -30,29 +29,29 @@ export const loadSourceMaps = async bundleDir => { try { const file = basename(path).replace('.map', ''); const consumer = await new SourceMapConsumer( - JSON.parse(fs.readFileSync(path, 'utf8'))); + JSON.parse(fs.readFileSync(path, 'utf8')), + ); sourceMaps.push({ file, consumer }); - } - catch (err) { + } catch (err) { logger.error(err); } } logger.log(`loaded ${sourceMaps.length} source maps`); -}; +} -export const retrace = stack => { +export function retrace(stack) { if (typeof stack !== 'string') { logger.log('ERROR: Stack is not a string!', stack); return stack; } const header = stack.split(/\n\s.*at/)[0]; const mappedStack = parseStackTrace(stack) - .map(frame => { + .map((frame) => { if (!frame.file) { return frame; } // Find the correct source map - const sourceMap = sourceMaps.find(sourceMap => { + const sourceMap = sourceMaps.find((sourceMap) => { return frame.file.includes(sourceMap.file); }); if (!sourceMap) { @@ -72,17 +71,17 @@ export const retrace = stack => { column: mappedFrame.column, }; }) - .map(frame => { + .map((frame) => { // Stringify the frame const { file, methodName, lineNumber } = frame; if (!file) { return ` at ${methodName}`; } const compactPath = file - .replace(/^webpack:\/\/\/?/, './') + .replace(/^rspack:\/\/\/?/, './') .replace(/.*node_modules\//, ''); return ` at ${methodName} (${compactPath}:${lineNumber})`; }) .join('\n'); return header + '\n' + mappedStack; -}; +} diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js index 87a8a5911b..c4104489f6 100644 --- a/tgui/packages/tgui-dev-server/link/server.js +++ b/tgui/packages/tgui-dev-server/link/server.js @@ -4,13 +4,12 @@ * @license MIT */ -import http from 'http'; -import { inspect } from 'util'; -import { createLogger, directLog } from '../logging.js'; -import { require } from '../require.js'; -import { loadSourceMaps, retrace } from './retrace.js'; +import { inspect } from 'node:util'; -const WebSocket = require('ws'); +import * as WebSocket from 'ws'; + +import { createLogger, directLog } from '../logging.js'; +import { loadSourceMaps, retrace } from './retrace.js'; const logger = createLogger('link'); @@ -18,23 +17,26 @@ const DEBUG = process.argv.includes('--debug'); export { loadSourceMaps }; -export const setupLink = () => new LinkServer(); +export function setupLink() { + return new LinkServer(); +} class LinkServer { constructor() { logger.log('setting up'); + /** @type {WebSocket.Server | null} */ this.wss = null; this.setupWebSocketLink(); - this.setupHttpLink(); } // WebSocket-based client link setupWebSocketLink() { const port = 3000; - this.wss = new WebSocket.Server({ port }); - this.wss.on('connection', ws => { + this.wss = new WebSocket.WebSocketServer({ port }); + + this.wss.on('connection', (ws) => { logger.log('client connected'); - ws.on('message', json => { + ws.on('message', (json) => { const msg = deserializeObject(json); this.handleLinkMessage(ws, msg); }); @@ -45,29 +47,10 @@ class LinkServer { logger.log(`listening on port ${port} (WebSocket)`); } - // One way HTTP-based client link for IE8 - setupHttpLink() { - const port = 3001; - this.httpServer = http.createServer((req, res) => { - if (req.method === 'POST') { - let body = ''; - req.on('data', chunk => { - body += chunk.toString(); - }); - req.on('end', () => { - const msg = deserializeObject(body); - this.handleLinkMessage(null, msg); - res.end(); - }); - return; - } - res.write('Hello'); - res.end(); - }); - this.httpServer.listen(port); - logger.log(`listening on port ${port} (HTTP)`); - } - + /** + * @param {WebSocket.Client} ws + * @param {WebSocket.MessageEvent} msg + */ handleLinkMessage(ws, msg) { const { type, payload } = msg; if (type === 'log') { @@ -76,19 +59,26 @@ class LinkServer { if (level <= 0 && !DEBUG) { return; } - directLog(ns, ...args.map(arg => { - if (typeof arg === 'object') { - return inspect(arg, { - depth: Infinity, - colors: true, - compact: 8, - }); - } - return arg; - })); + + directLog( + ns, + ...args.map((arg) => { + if (typeof arg === 'object') { + return inspect(arg, { + depth: Infinity, + colors: true, + compact: 8, + }); + } + return arg; + }), + ); return; } if (type === 'relay') { + if (!this.wss) { + return; + } for (let client of this.wss.clients) { if (client === ws) { continue; @@ -105,6 +95,9 @@ class LinkServer { } broadcastMessage(msg) { + if (!this.wss) { + return; + } const clients = [...this.wss.clients]; if (clients.length === 0) { return; @@ -117,7 +110,7 @@ class LinkServer { } } -const deserializeObject = str => { +function deserializeObject(str) { return JSON.parse(str, (key, value) => { if (typeof value === 'object' && value !== null) { if (value.__undefined__) { @@ -140,4 +133,4 @@ const deserializeObject = str => { } return value; }); -}; +} diff --git a/tgui/packages/tgui-dev-server/logging.js b/tgui/packages/tgui-dev-server/logging.js index 0dc222ae3d..0c6950bbf1 100644 --- a/tgui/packages/tgui-dev-server/logging.js +++ b/tgui/packages/tgui-dev-server/logging.js @@ -7,22 +7,22 @@ const inception = Date.now(); // Runtime detection -const isNode = process && process.release && process.release.name === 'node'; +const isNode = process?.release?.name === 'node'; let isChrome = false; try { isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); -} -catch {} +} catch {} // Timestamping function -const getTimestamp = () => { +function getTimestamp() { const timestamp = String(Date.now() - inception) .padStart(4, '0') .padStart(7, ' '); const seconds = timestamp.substr(0, timestamp.length - 3); const millis = timestamp.substr(-3); + return `${seconds}.${millis}`; -}; +} const getPrefix = (() => { if (isNode) { @@ -32,7 +32,7 @@ const getPrefix = (() => { bright: '\x1b[37;1m', reset: '\x1b[0m', }; - return ns => [ + return (ns) => [ `${ESC.dimmed}${getTimestamp()} ${ESC.bright}${ns}${ESC.reset}`, ]; } @@ -42,31 +42,33 @@ const getPrefix = (() => { dimmed: 'color: #888', bright: 'font-weight: bold', }; - return ns => [ + return (ns) => [ `%c${getTimestamp()}%c ${ns}`, styles.dimmed, styles.bright, ]; } - return ns => [ - `${getTimestamp()} ${ns}`, - ]; + + return (ns) => [`${getTimestamp()} ${ns}`]; })(); /** * Creates a logger object. */ -export const createLogger = ns => ({ - log: (...args) => console.log(...getPrefix(ns), ...args), - trace: (...args) => console.trace(...getPrefix(ns), ...args), - debug: (...args) => console.debug(...getPrefix(ns), ...args), - info: (...args) => console.info(...getPrefix(ns), ...args), - warn: (...args) => console.warn(...getPrefix(ns), ...args), - error: (...args) => console.error(...getPrefix(ns), ...args), -}); +export function createLogger(ns) { + return { + log: (...args) => console.log(...getPrefix(ns), ...args), + trace: (...args) => console.trace(...getPrefix(ns), ...args), + debug: (...args) => console.debug(...getPrefix(ns), ...args), + info: (...args) => console.info(...getPrefix(ns), ...args), + warn: (...args) => console.warn(...getPrefix(ns), ...args), + error: (...args) => console.error(...getPrefix(ns), ...args), + }; +} /** * Explicitly log with chosen namespace. */ -export const directLog = (ns, ...args) => +export function directLog(ns, ...args) { console.log(...getPrefix(ns), ...args); +} diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 8e477cd279..e908a1eeec 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -1,13 +1,16 @@ { - "private": true, - "name": "tgui-dev-server", - "version": "4.3.0", - "type": "module", - "dependencies": { - "axios": "^1.8.4", - "glob": "^13.0.0", - "source-map": "^0.7.6", - "stacktrace-parser": "^0.1.11", - "ws": "^8.18.3" - } + "private": true, + "name": "tgui-dev-server", + "version": "6.0.0", + "type": "module", + "dependencies": { + "@rspack/core": "^1.3.7", + "axios": "^1.8.4", + "source-map": "^0.7.4", + "stacktrace-parser": "^0.1.11", + "ws": "^8.18.1" + }, + "devDependencies": { + "@types/ws": "^8.18.1" + } } diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.js index c2bc552e00..c4d26b95b3 100644 --- a/tgui/packages/tgui-dev-server/reloader.js +++ b/tgui/packages/tgui-dev-server/reloader.js @@ -4,9 +4,10 @@ * @license MIT */ -import fs from 'fs'; -import os from 'os'; -import { basename } from 'path'; +import fs from 'node:fs'; +import os from 'node:os'; +import { basename } from 'node:path'; + import { DreamSeeker } from './dreamseeker.js'; import { createLogger } from './logging.js'; import { resolveGlob, resolvePath } from './util.js'; @@ -30,7 +31,7 @@ const SEARCH_LOCATIONS = [ let cacheRoot; -export const findCacheRoot = async () => { +export async function findCacheRoot() { if (cacheRoot) { return cacheRoot; } @@ -50,49 +51,53 @@ export const findCacheRoot = async () => { // Query the Windows Registry if (process.platform === 'win32') { logger.log('querying windows registry'); - let userpath = await regQuery( - 'HKCU\\Software\\Dantom\\BYOND', - 'userpath'); + let userpath = await regQuery('HKCU\\Software\\Dantom\\BYOND', 'userpath'); if (userpath) { - cacheRoot = userpath - .replace(/\\$/, '') - .replace(/\\/g, '/') - + '/cache'; + cacheRoot = userpath.replace(/\\$/, '').replace(/\\/g, '/') + '/cache'; onCacheRootFound(cacheRoot); return cacheRoot; } } logger.log('found no cache directories'); -}; +} -const onCacheRootFound = cacheRoot => { +function onCacheRootFound(cacheRoot) { logger.log(`found cache at '${cacheRoot}'`); -}; + // Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 514. + fs.closeSync(fs.openSync(cacheRoot + '/dummy.htm', 'w')); +} -export const reloadByondCache = async bundleDir => { +export async function reloadByondCache(bundleDir) { const cacheRoot = await findCacheRoot(); if (!cacheRoot) { return; } // Find tmp folders in cache - const cacheDirs = await resolveGlob(cacheRoot, './tmp*'); + const cacheDirs = resolveGlob(cacheRoot, './tmp*'); if (cacheDirs.length === 0) { logger.log('found no tmp folder in cache'); return; } - // Get dreamseeker instances - const pids = cacheDirs.map(cacheDir => ( - parseInt(cacheDir.split('/cache/tmp').pop(), 10) - )); + + const pids = cacheDirs.map((cacheDir) => { + return parseInt(cacheDir.split('\\cache\\tmp')[1], 10); + }); + const dssPromise = DreamSeeker.getInstancesByPids(pids); // Copy assets - const assets = await resolveGlob(bundleDir, './*.+(bundle|chunk|hot-update).*'); + const assets = await resolveGlob( + bundleDir, + './*.+(bundle|chunk|hot-update).*', + ); for (let cacheDir of cacheDirs) { // Clear garbage - const garbage = await resolveGlob(cacheDir, './*.+(bundle|chunk|hot-update).*'); + const garbage = await resolveGlob( + cacheDir, + './*.+(bundle|chunk|hot-update).*', + ); try { - // Plant a dummy browser window file, we'll be using this to avoid world topic - fs.closeSync(fs.openSync(cacheDir + '/dummy', 'w')); + // Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 515-516. + fs.closeSync(fs.openSync(cacheDir + '/dummy.htm', 'w')); for (let file of garbage) { fs.unlinkSync(file); @@ -103,8 +108,7 @@ export const reloadByondCache = async bundleDir => { fs.writeFileSync(destination, fs.readFileSync(asset)); } logger.log(`copied ${assets.length} files to '${cacheDir}'`); - } - catch (err) { + } catch (err) { logger.error(`failed copying to '${cacheDir}'`); logger.error(err); } @@ -120,4 +124,4 @@ export const reloadByondCache = async bundleDir => { }); } } -}; +} diff --git a/tgui/packages/tgui-dev-server/require.js b/tgui/packages/tgui-dev-server/require.js index 0551d630fc..d1f5ec4ce1 100644 --- a/tgui/packages/tgui-dev-server/require.js +++ b/tgui/packages/tgui-dev-server/require.js @@ -4,6 +4,6 @@ * @license MIT */ -import { createRequire } from 'module'; +import { createRequire } from 'node:module'; export const require = createRequire(import.meta.url); diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.js index 13fbef3b21..2668084bc1 100644 --- a/tgui/packages/tgui-dev-server/util.js +++ b/tgui/packages/tgui-dev-server/util.js @@ -4,30 +4,25 @@ * @license MIT */ -import fs from 'fs'; -import path from 'path'; - -import { require } from './require.js'; - -const globPkg = require('glob'); +import fs, { globSync } from 'node:fs'; +import path from 'node:path'; export const resolvePath = path.resolve; -/** - * Combines path.resolve with glob patterns. - */ -export const resolveGlob = (...sections) => { - const unsafePaths = globPkg.sync(path.resolve(...sections), { - strict: false, - silent: true, - windowsPathsNoEscape: true, - }); +/** Combines path.resolve with glob patterns. */ +export function resolveGlob(...sections) { + /** @type {string[]} */ + const unsafePaths = globSync(path.resolve(...sections)); + + /** @type {string[]} */ const safePaths = []; + for (let path of unsafePaths) { try { fs.statSync(path); safePaths.push(path); } catch {} } + return safePaths; -}; +} diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js index 8cba68afcb..1fd81ae780 100644 --- a/tgui/packages/tgui-dev-server/webpack.js +++ b/tgui/packages/tgui-dev-server/webpack.js @@ -4,40 +4,49 @@ * @license MIT */ -import fs from 'fs'; -import { createRequire } from 'module'; -import { dirname } from 'path'; +import fs from 'node:fs'; +import { createRequire } from 'node:module'; + import { loadSourceMaps, setupLink } from './link/server.js'; import { createLogger } from './logging.js'; import { reloadByondCache } from './reloader.js'; import { resolveGlob } from './util.js'; -const logger = createLogger('webpack'); +const logger = createLogger('rspack'); /** * @param {any} config - * @return {WebpackCompiler} + * @return {RspackCompiler} */ -export const createCompiler = async options => { - const compiler = new WebpackCompiler(); +export async function createCompiler(options) { + const compiler = new RspackCompiler(); await compiler.setup(options); - return compiler; -}; -class WebpackCompiler { + return compiler; +} + +class RspackCompiler { async setup(options) { // Create a require context that is relative to project root // and retrieve all necessary dependencies. - const requireFromRoot = createRequire(dirname(import.meta.url) + '/../..'); - const webpack = await requireFromRoot('webpack'); - const createConfig = await requireFromRoot('./webpack.config.js'); + const requireFromRoot = createRequire(import.meta.dirname + '/../../..'); + /** @type {typeof import('@rspack/core')} */ + const rspack = await requireFromRoot('@rspack/core'); + + const createConfig = await requireFromRoot('./rspack.config.cjs'); + const createDevConfig = await requireFromRoot('./rspack.config-dev.cjs'); + const config = createConfig({}, options); + const devConfig = createDevConfig({}, options); + + const mergedConfig = { ...config, ...devConfig }; + // Inject the HMR plugin into the config if we're using it if (options.hot) { - config.plugins.push(new webpack.HotModuleReplacementPlugin()); + mergedConfig.plugins.push(new rspack.HotModuleReplacementPlugin()); } - this.webpack = webpack; - this.config = config; + this.rspack = rspack; + this.config = mergedConfig; this.bundleDir = config.output.path; } @@ -46,7 +55,7 @@ class WebpackCompiler { // Setup link const link = setupLink(); // Instantiate the compiler - const compiler = this.webpack.webpack(this.config); + const compiler = this.rspack.rspack(this.config); // Clear garbage before compiling compiler.hooks.watchRun.tapPromise('tgui-dev-server', async () => { const files = await resolveGlob(this.bundleDir, './*.hot-update.*'); @@ -57,7 +66,7 @@ class WebpackCompiler { logger.log('compiling'); }); // Start reloading when it's finished - compiler.hooks.done.tap('tgui-dev-server', async stats => { + compiler.hooks.done.tap('tgui-dev-server', async (stats) => { // Load source maps await loadSourceMaps(this.bundleDir); // Reload cache @@ -75,9 +84,9 @@ class WebpackCompiler { return; } stats - .toString(this.config.devServer.stats) + ?.toString(this.config.devServer.stats) .split('\n') - .forEach(line => logger.log(line)); + .forEach((line) => logger.log(line)); }); } } diff --git a/tgui/packages/tgui-dev-server/winreg.js b/tgui/packages/tgui-dev-server/winreg.js index 669e2aad55..e8c3561ebe 100644 --- a/tgui/packages/tgui-dev-server/winreg.js +++ b/tgui/packages/tgui-dev-server/winreg.js @@ -6,42 +6,49 @@ * @license MIT */ -import { exec } from 'child_process'; -import { promisify } from 'util'; +import { exec } from 'node:child_process'; +import { promisify } from 'node:util'; + import { createLogger } from './logging.js'; const logger = createLogger('winreg'); -export const regQuery = async (path, key) => { +/** + * Query a registry key. + * @param {string} path + * @param {string} key + * @return {Promise} + */ +export async function regQuery(path, key) { if (process.platform !== 'win32') { - return null; + return; } try { const command = `reg query "${path}" /v ${key}`; const { stdout } = await promisify(exec)(command); const keyPattern = ` ${key} `; const indexOfKey = stdout.indexOf(keyPattern); + if (indexOfKey === -1) { logger.error('could not find the registry key'); - return null; + return; } + const indexOfEol = stdout.indexOf('\r\n', indexOfKey); if (indexOfEol === -1) { logger.error('could not find the end of the line'); - return null; + return; } - const indexOfValue = stdout.indexOf( - ' ', - indexOfKey + keyPattern.length); + + const indexOfValue = stdout.indexOf(' ', indexOfKey + keyPattern.length); if (indexOfValue === -1) { logger.error('could not find the start of the key value'); - return null; + return; } - const value = stdout.substring(indexOfValue + 4, indexOfEol); - return value; - } - catch (err) { + + return stdout.substring(indexOfValue + 4, indexOfEol); + } catch (err) { logger.error(err); - return null; + return; } -}; +}