UNPKG

feroom

Version:
345 lines (326 loc) 15.5 kB
'use strict'; var moost = require('moost'); var eventCli = require('@moostjs/event-cli'); var tools = require('@feroomjs/tools'); var server = require('@feroomjs/server'); var eventHttp = require('@moostjs/event-http'); var http = require('http'); var WebSocket = require('ws'); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } const banner = () => `[${"feroom"}][${new Date().toISOString().replace('T', ' ').replace(/\.\d{3}z$/i, '')}] `; /* istanbul ignore file */ function logError(error) { console.error('' + '' + banner() + error + ''); } function panic(error) { logError(error); return new Error(error); } function isTextFile(path) { return path.endsWith('.js') || path.endsWith('.map') || path.endsWith('.css') || path.endsWith('.json') || path.endsWith('.txt') || path.endsWith('.mjs') || path.endsWith('.cjs') || path.endsWith('.md') || path.endsWith('.html'); } let CliBuild = class CliBuild { build() { return __awaiter(this, void 0, void 0, function* () { tools.logger.title('FeRoom Build'); if (typeof this.configPath !== 'undefined' && typeof this.configPath !== 'string') throw panic('Key -c must have string value.'); yield tools.esBuildBundle(this.configPath); tools.logger.info('\n✔ Build done'); return ''; }); } }; __decorate([ eventCli.CliParam(['c', 'configPath'], 'Path to the FeRoom Config file.') // @Validate(({ value: v }) => typeof v !== 'undefined' && typeof v !== 'string' ? 'string value expected with path to FeRoom Config file.' : true) , __metadata("design:type", String) ], CliBuild.prototype, "configPath", void 0); __decorate([ eventCli.Cli(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], CliBuild.prototype, "build", null); CliBuild = __decorate([ moost.Injectable('FOR_EVENT'), moost.Controller() ], CliBuild); const protocol = 'feroom-dev'; let WsExt = class WsExt { constructor(server, reg) { this.clients = new Set(); const wss = new WebSocket.Server({ noServer: true }); server.on('upgrade', (req, socket, head) => { if (req.headers['sec-websocket-protocol'] !== protocol) { return; } wss.handleUpgrade(req, socket, head, (client) => { wss.emit('connection', client, req); }); }); reg.on('register-module', (module) => { this.broadcastMessage({ command: 'reload', module: module.id, version: module.version }); }); wss.on('connection', (client) => { this.connectClient(client); this.registerListener(client); }); } injectIndexBody() { return `<script> const socketURL = (location.protocol === "http:" ? "ws://" : "wss://") + location.host + "/"; const socket = new WebSocket(socketURL, "${protocol}"); socket.addEventListener("message", (data) => { console.log('${'' + ''}Got message: reloading...${''}', data) window.location.reload() }) </script> `; } broadcastMessage(data) { this.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(data)); } else { this.disconnectClient(client); } }); } disconnectClient(client) { client.terminate(); this.clients.delete(client); } disconnectAllClients() { for (const client of this.clients) { this.disconnectClient(client); } } registerListener(client) { // eslint-disable-next-line @typescript-eslint/no-unused-vars client.on('message', (data) => { // }); } connectClient(client) { this.clients.add(client); } }; WsExt = __decorate([ server.FeRoomExtension('ws-ext'), moost.Controller(), __metadata("design:paramtypes", [http.Server, server.FeRegistry]) ], WsExt); let CliDev = class CliDev { dev() { return __awaiter(this, void 0, void 0, function* () { tools.logger.title('FeRoom Dev Server'); if (typeof this.configPath !== 'undefined' && typeof this.configPath !== 'string') throw panic('Key -c must has string value.'); const config = new tools.FeRoomConfigFile(this.configPath, true); yield fireDevServer(); function fireDevServer() { var _a, _b, _c, _d; return __awaiter(this, void 0, void 0, function* () { let configData = yield config.get(); const devServer = { port: ((_a = configData.devServer) === null || _a === void 0 ? void 0 : _a.port) || 3000, shared: ((_b = configData.devServer) === null || _b === void 0 ? void 0 : _b.shared) || '', feroom: Object.assign({}, (((_c = configData.devServer) === null || _c === void 0 ? void 0 : _c.feroom) || {})), ext: ((_d = configData.devServer) === null || _d === void 0 ? void 0 : _d.ext) || [], }; // const listeners: (() => void)[] = [] let target = ''; if (devServer.shared) { tools.logger.step('Connecting to shared dev server: ' + devServer.shared); tools.logger.warn('devServer.feroom and devServer.ext options will be ignored, because shared dev server option is picked.'); target = devServer.shared; } else { const reg = new server.FeRegistry(); const server$1 = new server.FeRoom({}, reg); // server.setProvideRegistry(createProvideRegistry(['feroom-dev-server-event', () => (cb: () => void) => { listeners.push(cb) }])) for (const ext of devServer.ext) { void server$1.ext(ext); } void server$1.ext(WsExt); tools.logger.step('Starting dev server...'); target = `http://localhost:${devServer.port}`; void server$1.adapter(new eventHttp.MoostHttp()).listen(devServer.port, () => tools.logger.dev('FeRoom dev server is up: ' + target)); yield server$1.init(); } tools.logger.step('Building bundle...'); let ctx; yield triggerChange(configData); config.onChange((newConfig) => __awaiter(this, void 0, void 0, function* () { tools.logger.clear(); tools.logger.title('Config change detected.'); yield triggerChange(newConfig); })); function triggerChange(newConfig) { return __awaiter(this, void 0, void 0, function* () { if (!ctx) { tools.logger.step('Creqting a new build context'); ctx = yield tools.esBuildCtx(config, (result) => __awaiter(this, void 0, void 0, function* () { tools.logger.clear(); const outputFiles = {}; (result.outputFiles || []).forEach(f => outputFiles[tools.unbuildPath(f.path)] = isTextFile(f.path) ? Buffer.from(f.contents).toString() : Buffer.from(f.contents)); // const fr = new tools.FeRoomRegister({ host: target }); yield fr.register({ activate: true, conf: config, files: outputFiles, }); tools.logger.dev('Waiting for changes...'); // listeners.forEach(l => l()) })); yield ctx.watch(); } else if (JSON.stringify(configData.buildOptions) !== JSON.stringify(newConfig.buildOptions)) { // re-build from scratch required tools.logger.dev('buildOptions changed, full re-build required'); configData = newConfig; yield ctx.cancel(); yield ctx.dispose(); ctx = undefined; yield triggerChange(newConfig); } else { yield ctx.rebuild(); } }); } }); } return ''; }); } }; __decorate([ eventCli.CliParam(['c', 'configPath'], 'Path to the FeRoom Config file.'), moost.Validate(({ value: v }) => typeof v !== 'undefined' && typeof v !== 'string' ? 'string value expected with path to FeRoom Config file.' : true), __metadata("design:type", String) ], CliDev.prototype, "configPath", void 0); __decorate([ eventCli.Cli(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], CliDev.prototype, "dev", null); CliDev = __decorate([ moost.Injectable('FOR_EVENT'), moost.Controller() ], CliDev); let CliRegister = class CliRegister { register() { return __awaiter(this, void 0, void 0, function* () { tools.logger.title('FeRoom Register'); if (typeof this.configPath !== 'undefined' && typeof this.configPath !== 'string') throw panic('Key -c must have string value.'); if (!this.host || typeof this.host !== 'string') throw panic('Key -h required with the target host of FeRoom server.'); if (!this.host.startsWith('http')) throw panic(`Host "${this.host}" has wrong format. It must start with "http".`); if (this.host.search('://') < 4) throw panic(`Host "${this.host}" has wrong format. Use full host string like "http://localhost:3000" etc...`); tools.logger.info('Target host: ' + this.host); const fr = new tools.FeRoomRegister({ host: this.host }); yield fr.register({ activate: true, conf: this.configPath, }); return ''; }); } }; __decorate([ eventCli.CliParam(['c', 'configPath'], 'Path to the FeRoom Config file.'), moost.Validate(({ value: v }) => typeof v !== 'undefined' && typeof v !== 'string' ? 'string value expected with path to FeRoom Config file.' : true), __metadata("design:type", String) ], CliRegister.prototype, "configPath", void 0); __decorate([ moost.Required(), eventCli.CliParam(['h', 'host'], 'FeRoom server Host to register module.'), __metadata("design:type", String) ], CliRegister.prototype, "host", void 0); __decorate([ eventCli.Cli(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], CliRegister.prototype, "register", null); CliRegister = __decorate([ moost.Injectable('FOR_EVENT'), moost.Controller() ], CliRegister); class FeRoomCli extends moost.Moost { constructor() { super({ silent: true }); this.registerControllers(CliBuild, CliRegister, CliDev); this.applyGlobalInterceptors((before, after, onError) => { onError((error) => { console.error(error.message); console.error(error.stack); process.exit(1); }); }); this.applyGlobalPipes(moost.validatePipe()); } } const cli = () => { supressNodeWarnings(); moost.getMoostInfact().silent(); const app = new FeRoomCli(); const cli = new eventCli.MoostCli(); app.adapter(cli); void app.init(); }; function supressNodeWarnings() { // eslint-disable-next-line @typescript-eslint/unbound-method const { emitWarning } = process; process.emitWarning = (warning, ...args) => { if (args[0] === 'ExperimentalWarning') { return; } if (args[0] && typeof args[0] === 'object' && args[0].type === 'ExperimentalWarning') { return; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return emitWarning(warning, ...args); }; } exports.cli = cli;