@gravity-ui/app-builder 0.28.0 → 0.28.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,8 +34,70 @@ const rimraf_1 = require("rimraf");
34
34
  const utils_1 = require("../../common/utils");
35
35
  const logger_1 = __importDefault(require("../../common/logger"));
36
36
  const paths_1 = __importDefault(require("../../common/paths"));
37
+ // Импортируем систему табов для логирования
38
+ const TerminalTabs_1 = require("./ui/TerminalTabs");
39
+ // Глобальная система табов
40
+ let terminalTabs;
41
+ // Функция для добавления лога в систему табов
42
+ function addLogToTabs(type, message, level = 'message') {
43
+ if (terminalTabs && process.stdout.isTTY) {
44
+ terminalTabs.addLog({
45
+ type,
46
+ message,
47
+ timestamp: Date.now(),
48
+ level,
49
+ });
50
+ }
51
+ }
52
+ // Обёртка для logger с интеграцией в табы
53
+ function createLoggerWrapper(type) {
54
+ return {
55
+ message: (...args) => {
56
+ const message = args.join(' ');
57
+ addLogToTabs(type, message, 'message');
58
+ if (!process.stdout.isTTY) {
59
+ logger_1.default.message(`[${type}]`, message);
60
+ }
61
+ },
62
+ success: (...args) => {
63
+ const message = args.join(' ');
64
+ addLogToTabs(type, message, 'success');
65
+ if (!process.stdout.isTTY) {
66
+ logger_1.default.success(`[${type}]`, message);
67
+ }
68
+ },
69
+ warning: (...args) => {
70
+ const message = args.join(' ');
71
+ addLogToTabs(type, message, 'warning');
72
+ if (!process.stdout.isTTY) {
73
+ logger_1.default.warning(`[${type}]`, message);
74
+ }
75
+ },
76
+ error: (...args) => {
77
+ const message = args.join(' ');
78
+ addLogToTabs(type, message, 'error');
79
+ if (!process.stdout.isTTY) {
80
+ logger_1.default.error(`[${type}]`, message);
81
+ }
82
+ },
83
+ verbose: (...args) => {
84
+ const message = args.join(' ');
85
+ addLogToTabs(type, message, 'verbose');
86
+ if (!process.stdout.isTTY) {
87
+ logger_1.default.verbose(`[${type}]`, message);
88
+ }
89
+ },
90
+ };
91
+ }
37
92
  async function default_1(config) {
38
93
  process.env.NODE_ENV = 'development';
94
+ // Инициализируем систему табов только в TTY
95
+ if (process.stdout.isTTY) {
96
+ terminalTabs = new TerminalTabs_1.TerminalTabs();
97
+ terminalTabs.initialize();
98
+ // Добавляем начальный лог
99
+ addLogToTabs('all', 'Запуск режима разработки...', 'message');
100
+ }
39
101
  const shouldCompileClient = (0, utils_1.shouldCompileTarget)(config.target, 'client');
40
102
  const shouldCompileServer = (0, utils_1.shouldCompileTarget)(config.target, 'server');
41
103
  if (shouldCompileClient && shouldCompileServer) {
@@ -48,7 +110,8 @@ async function default_1(config) {
48
110
  const { inspect, inspectBrk } = config.server;
49
111
  const startNodemon = () => {
50
112
  if (needToStartNodemon && serverCompiled && clientCompiled) {
51
- logger_1.default.message('Starting application at', serverPath);
113
+ const serverLogger = createLoggerWrapper('server');
114
+ serverLogger.message('Starting application at', serverPath);
52
115
  const nodeArgs = ['--enable-source-maps'];
53
116
  if (inspect || inspectBrk) {
54
117
  nodeArgs.push(`--${inspect ? 'inspect' : 'inspect-brk'}=:::${inspect || inspectBrk}`);
@@ -77,6 +140,8 @@ async function default_1(config) {
77
140
  serverCompilation.onMessage((msg) => {
78
141
  if (typeof msg === 'object' && 'type' in msg && msg.type === 'Emitted') {
79
142
  serverCompiled = true;
143
+ const serverLogger = createLoggerWrapper('server');
144
+ serverLogger.success('Server compilation completed');
80
145
  startNodemon();
81
146
  }
82
147
  });
@@ -85,25 +150,38 @@ async function default_1(config) {
85
150
  if (shouldCompileClient) {
86
151
  const { watchClientCompilation } = await import('./client.js');
87
152
  clientCompilation = await watchClientCompilation(config, () => {
88
- logger_1.default.success('Manifest was compiled successfully');
153
+ const clientLogger = createLoggerWrapper('client');
154
+ clientLogger.success('Manifest was compiled successfully');
89
155
  clientCompiled = true;
90
156
  startNodemon();
91
157
  });
92
158
  }
93
- process.on('SIGINT', async () => {
94
- logger_1.default.success('\nCleaning up...');
159
+ const cleanup = async () => {
160
+ if (terminalTabs) {
161
+ terminalTabs.destroy();
162
+ }
95
163
  await serverCompilation?.stop('SIGINT');
96
164
  await clientCompilation?.stop();
165
+ };
166
+ process.on('SIGINT', async () => {
167
+ if (!process.stdout.isTTY) {
168
+ logger_1.default.success('\nCleaning up...');
169
+ }
170
+ await cleanup();
97
171
  process.exit(1);
98
172
  });
99
173
  process.on('SIGTERM', async () => {
100
- logger_1.default.success('\nCleaning up...');
101
- await serverCompilation?.stop('SIGTERM');
102
- await clientCompilation?.stop();
174
+ if (!process.stdout.isTTY) {
175
+ logger_1.default.success('\nCleaning up...');
176
+ }
177
+ await cleanup();
103
178
  process.exit(1);
104
179
  });
105
180
  (0, signal_exit_1.onExit)((_code, signal) => {
106
181
  serverCompilation?.stop(signal);
107
182
  clientCompilation?.stop();
183
+ if (terminalTabs) {
184
+ terminalTabs.destroy();
185
+ }
108
186
  });
109
187
  }
@@ -0,0 +1,13 @@
1
+ import { EventEmitter } from 'events';
2
+ import { BaseLogger } from '../../../common/logger';
3
+ import type { LogEntry } from './TerminalTabs';
4
+ export declare class LogCollector extends EventEmitter {
5
+ private logs;
6
+ private maxLogs;
7
+ constructor(maxLogs?: number);
8
+ addLog(entry: LogEntry): void;
9
+ getLogs(): LogEntry[];
10
+ clear(): void;
11
+ createLogger(type: 'server' | 'client', namespace?: string, verbose?: boolean): BaseLogger;
12
+ }
13
+ export declare const globalLogCollector: LogCollector;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.globalLogCollector = exports.LogCollector = void 0;
4
+ const events_1 = require("events");
5
+ const logger_1 = require("../../../common/logger");
6
+ class LogCollector extends events_1.EventEmitter {
7
+ logs = [];
8
+ maxLogs = 1000;
9
+ constructor(maxLogs = 1000) {
10
+ super();
11
+ this.maxLogs = maxLogs;
12
+ }
13
+ addLog(entry) {
14
+ this.logs.push(entry);
15
+ if (this.logs.length > this.maxLogs) {
16
+ this.logs = this.logs.slice(-this.maxLogs);
17
+ }
18
+ this.emit('log', entry);
19
+ }
20
+ getLogs() {
21
+ return [...this.logs];
22
+ }
23
+ clear() {
24
+ this.logs = [];
25
+ this.emit('clear');
26
+ }
27
+ createLogger(type, namespace, verbose = false) {
28
+ const logger = new logger_1.Logger(namespace, verbose);
29
+ // Перехватываем методы логирования
30
+ const originalMessage = logger.message.bind(logger);
31
+ const originalSuccess = logger.success.bind(logger);
32
+ const originalWarning = logger.warning.bind(logger);
33
+ const originalError = logger.error.bind(logger);
34
+ const originalVerbose = logger.verbose.bind(logger);
35
+ logger.message = (...args) => {
36
+ const message = args.join(' ');
37
+ this.addLog({
38
+ type,
39
+ message,
40
+ timestamp: Date.now(),
41
+ level: 'message',
42
+ });
43
+ return originalMessage(...args);
44
+ };
45
+ logger.success = (...args) => {
46
+ const message = args.join(' ');
47
+ this.addLog({
48
+ type,
49
+ message,
50
+ timestamp: Date.now(),
51
+ level: 'success',
52
+ });
53
+ return originalSuccess(...args);
54
+ };
55
+ logger.warning = (...args) => {
56
+ const message = args.join(' ');
57
+ this.addLog({
58
+ type,
59
+ message,
60
+ timestamp: Date.now(),
61
+ level: 'warning',
62
+ });
63
+ return originalWarning(...args);
64
+ };
65
+ logger.error = (...args) => {
66
+ const message = args.join(' ');
67
+ this.addLog({
68
+ type,
69
+ message,
70
+ timestamp: Date.now(),
71
+ level: 'error',
72
+ });
73
+ return originalError(...args);
74
+ };
75
+ logger.verbose = (...args) => {
76
+ const message = args.join(' ');
77
+ this.addLog({
78
+ type,
79
+ message,
80
+ timestamp: Date.now(),
81
+ level: 'verbose',
82
+ });
83
+ return originalVerbose(...args);
84
+ };
85
+ return logger;
86
+ }
87
+ }
88
+ exports.LogCollector = LogCollector;
89
+ exports.globalLogCollector = new LogCollector();
@@ -0,0 +1,25 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface LogEntry {
3
+ type: 'server' | 'client' | 'all';
4
+ message: string;
5
+ timestamp: number;
6
+ level: 'message' | 'success' | 'warning' | 'error' | 'verbose';
7
+ }
8
+ export declare class TerminalTabs extends EventEmitter {
9
+ private logs;
10
+ private activeTab;
11
+ private maxLogs;
12
+ private rl;
13
+ private isInitialized;
14
+ constructor(maxLogs?: number);
15
+ initialize(): void;
16
+ addLog(entry: LogEntry): void;
17
+ destroy(): void;
18
+ private setupKeyHandlers;
19
+ private clearLogs;
20
+ private render;
21
+ private renderTabs;
22
+ private renderLogs;
23
+ private formatLogMessage;
24
+ private renderHelp;
25
+ }
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.TerminalTabs = void 0;
27
+ const readline = __importStar(require("readline"));
28
+ const events_1 = require("events");
29
+ // ANSI escape codes для цветов
30
+ const ansi = {
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ dim: '\x1b[2m',
34
+ cyan: '\x1b[36m',
35
+ blue: '\x1b[34m',
36
+ green: '\x1b[32m',
37
+ yellow: '\x1b[33m',
38
+ red: '\x1b[31m',
39
+ white: '\x1b[37m',
40
+ bgBlue: '\x1b[44m',
41
+ };
42
+ const tabs = [
43
+ { key: 'all', title: 'Все логи', filter: () => true },
44
+ { key: 'server', title: 'Сервер', filter: (log) => log.type === 'server' },
45
+ { key: 'client', title: 'Клиент', filter: (log) => log.type === 'client' },
46
+ ];
47
+ class TerminalTabs extends events_1.EventEmitter {
48
+ logs = [];
49
+ activeTab = 0;
50
+ maxLogs = 1000;
51
+ rl;
52
+ isInitialized = false;
53
+ constructor(maxLogs = 1000) {
54
+ super();
55
+ this.maxLogs = maxLogs;
56
+ this.rl = readline.createInterface({
57
+ input: process.stdin,
58
+ output: process.stdout,
59
+ });
60
+ this.setupKeyHandlers();
61
+ }
62
+ initialize() {
63
+ if (this.isInitialized)
64
+ return;
65
+ this.isInitialized = true;
66
+ console.clear();
67
+ this.render();
68
+ }
69
+ addLog(entry) {
70
+ this.logs.push(entry);
71
+ if (this.logs.length > this.maxLogs) {
72
+ this.logs = this.logs.slice(-this.maxLogs);
73
+ }
74
+ // Обновляем только если таб инициализирован
75
+ if (this.isInitialized) {
76
+ this.render();
77
+ }
78
+ }
79
+ destroy() {
80
+ if (process.stdin.isTTY) {
81
+ process.stdin.setRawMode(false);
82
+ }
83
+ this.rl.close();
84
+ }
85
+ setupKeyHandlers() {
86
+ if (process.stdin.isTTY) {
87
+ process.stdin.setRawMode(true);
88
+ process.stdin.setEncoding('utf8');
89
+ process.stdin.on('data', (key) => {
90
+ // Ctrl+C
91
+ if (key === '\u0003') {
92
+ process.exit();
93
+ }
94
+ switch (key) {
95
+ case '\u001b[D': // Стрелка влево
96
+ case 'h':
97
+ this.activeTab = this.activeTab > 0 ? this.activeTab - 1 : tabs.length - 1;
98
+ this.render();
99
+ break;
100
+ case '\u001b[C': // Стрелка вправо
101
+ case 'l':
102
+ this.activeTab = this.activeTab < tabs.length - 1 ? this.activeTab + 1 : 0;
103
+ this.render();
104
+ break;
105
+ case 'c':
106
+ this.clearLogs();
107
+ break;
108
+ case 'q':
109
+ process.exit();
110
+ break;
111
+ }
112
+ });
113
+ }
114
+ }
115
+ clearLogs() {
116
+ this.logs = [];
117
+ this.render();
118
+ }
119
+ render() {
120
+ if (!process.stdout.isTTY)
121
+ return;
122
+ // Очищаем экран
123
+ console.clear();
124
+ // Рендерим табы
125
+ this.renderTabs();
126
+ // Рендерим логи для активного таба
127
+ this.renderLogs();
128
+ // Рендерим справку
129
+ this.renderHelp();
130
+ }
131
+ renderTabs() {
132
+ let tabsLine = '';
133
+ tabs.forEach((tab, index) => {
134
+ const isActive = index === this.activeTab;
135
+ const title = ` ${tab.title} `;
136
+ if (isActive) {
137
+ tabsLine += `${ansi.cyan}${ansi.bold}${ansi.bgBlue}${title}${ansi.reset}`;
138
+ }
139
+ else {
140
+ tabsLine += `${ansi.white}${title}${ansi.reset}`;
141
+ }
142
+ tabsLine += ' ';
143
+ });
144
+ console.log(tabsLine);
145
+ console.log(`${ansi.dim}${'─'.repeat(process.stdout.columns || 80)}${ansi.reset}`);
146
+ }
147
+ renderLogs() {
148
+ const currentTab = tabs[this.activeTab];
149
+ if (!currentTab)
150
+ return;
151
+ const filteredLogs = this.logs.filter(currentTab.filter);
152
+ const displayLogs = filteredLogs.slice(-40); // Показываем последние 40 логов
153
+ displayLogs.forEach((log) => {
154
+ console.log(this.formatLogMessage(log));
155
+ });
156
+ }
157
+ formatLogMessage(log) {
158
+ const timestamp = new Date(log.timestamp).toLocaleTimeString();
159
+ const prefix = `${ansi.dim}[${timestamp}] ${ansi.reset}`;
160
+ let colorCode = ansi.white;
161
+ switch (log.level) {
162
+ case 'success':
163
+ colorCode = ansi.green;
164
+ break;
165
+ case 'warning':
166
+ colorCode = ansi.yellow;
167
+ break;
168
+ case 'error':
169
+ colorCode = ansi.red;
170
+ break;
171
+ case 'verbose':
172
+ colorCode = ansi.dim;
173
+ break;
174
+ }
175
+ return `${prefix}${colorCode}${log.message}${ansi.reset}`;
176
+ }
177
+ renderHelp() {
178
+ const filteredCount = this.logs.filter(tabs[this.activeTab]?.filter || (() => true)).length;
179
+ const help = `${ansi.dim}\nУправление: ← → (или h/l) - переключение табов | c - очистить | q - выход | Показано: ${filteredCount} логов${ansi.reset}`;
180
+ console.log(help);
181
+ }
182
+ }
183
+ exports.TerminalTabs = TerminalTabs;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const TerminalTabs_1 = require("./TerminalTabs");
5
+ const tabs = new TerminalTabs_1.TerminalTabs();
6
+ // Инициализируем интерфейс табов
7
+ tabs.initialize();
8
+ // Симулируем логи от сервера и клиента
9
+ let logCounter = 0;
10
+ const addServerLog = () => {
11
+ tabs.addLog({
12
+ type: 'server',
13
+ message: `Server log message ${++logCounter}`,
14
+ timestamp: Date.now(),
15
+ level: Math.random() > 0.7 ? 'success' : 'message',
16
+ });
17
+ };
18
+ const addClientLog = () => {
19
+ tabs.addLog({
20
+ type: 'client',
21
+ message: `Client build progress ${++logCounter}`,
22
+ timestamp: Date.now(),
23
+ level: Math.random() > 0.8 ? 'warning' : 'message',
24
+ });
25
+ };
26
+ const addErrorLog = () => {
27
+ const type = Math.random() > 0.5 ? 'server' : 'client';
28
+ tabs.addLog({
29
+ type,
30
+ message: `Error in ${type}: Something went wrong ${++logCounter}`,
31
+ timestamp: Date.now(),
32
+ level: 'error',
33
+ });
34
+ };
35
+ // Добавляем начальные логи
36
+ tabs.addLog({
37
+ type: 'all',
38
+ message: 'Демонстрация системы табов запущена',
39
+ timestamp: Date.now(),
40
+ level: 'success',
41
+ });
42
+ // Симулируем активность
43
+ setInterval(() => {
44
+ const rand = Math.random();
45
+ if (rand > 0.7) {
46
+ addServerLog();
47
+ }
48
+ else if (rand > 0.4) {
49
+ addClientLog();
50
+ }
51
+ else if (rand > 0.9) {
52
+ addErrorLog();
53
+ }
54
+ }, 500);
55
+ // Обработка выхода
56
+ process.on('SIGINT', () => {
57
+ tabs.destroy();
58
+ console.log('\nДемонстрация завершена');
59
+ process.exit(0);
60
+ });
61
+ console.log('Нажмите Ctrl+C для выхода, стрелки влево/вправо для переключения табов, c для очистки');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/app-builder",
3
- "version": "0.28.0",
3
+ "version": "0.28.1-beta.0",
4
4
  "description": "Develop and build your React client-server projects, powered by typescript and webpack",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -75,15 +75,16 @@
75
75
  "@rspack/core": "1.3.9",
76
76
  "@rspack/dev-server": "^1.1.1",
77
77
  "@rspack/plugin-react-refresh": "^1.4.1",
78
- "@statoscope/webpack-plugin": "^5.29.0",
79
78
  "@statoscope/stats": "^5.28.1",
80
79
  "@statoscope/stats-extension-compressed": "^5.28.1",
81
80
  "@statoscope/webpack-model": "^5.29.0",
81
+ "@statoscope/webpack-plugin": "^5.29.0",
82
82
  "@svgr/core": "^8.1.0",
83
83
  "@svgr/plugin-jsx": "^8.1.0",
84
84
  "@svgr/webpack": "^8.1.0",
85
85
  "@swc/core": "1.11.24",
86
86
  "@swc/plugin-transform-imports": "7.0.3",
87
+ "@types/react": "^19.1.6",
87
88
  "babel-loader": "^9.2.1",
88
89
  "babel-plugin-import": "^1.13.8",
89
90
  "babel-plugin-inline-react-svg": "^2.0.2",
@@ -107,6 +108,8 @@
107
108
  "fork-ts-checker-webpack-plugin": "^9.0.2",
108
109
  "fs-extra": "^11.2.0",
109
110
  "get-port": "^7.1.0",
111
+ "ink": "^5.2.1",
112
+ "keypress": "^0.2.1",
110
113
  "mime-types": "^2.1.35",
111
114
  "mini-css-extract-plugin": "^2.9.1",
112
115
  "moment-timezone-data-webpack-plugin": "^1.5.1",