vladx 1.0.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.
Files changed (42) hide show
  1. package/README.md +256 -0
  2. package/bin/cli.js +486 -0
  3. package/bin/vlad.js +539 -0
  4. package/bin/vladpm.js +710 -0
  5. package/bin/vladx.js +491 -0
  6. package/package.json +57 -0
  7. package/src/engine/jit-compiler.js +285 -0
  8. package/src/engine/vladx-engine.js +941 -0
  9. package/src/index.js +44 -0
  10. package/src/interpreter/interpreter.js +2114 -0
  11. package/src/lexer/lexer.js +658 -0
  12. package/src/lexer/optimized-lexer.js +106 -0
  13. package/src/lexer/regex-cache.js +83 -0
  14. package/src/parser/ast-nodes.js +472 -0
  15. package/src/parser/parser.js +1408 -0
  16. package/src/runtime/advanced-type-system.js +209 -0
  17. package/src/runtime/async-manager.js +252 -0
  18. package/src/runtime/builtins.js +143 -0
  19. package/src/runtime/bundler.js +422 -0
  20. package/src/runtime/cache-manager.js +126 -0
  21. package/src/runtime/data-structures.js +612 -0
  22. package/src/runtime/debugger.js +260 -0
  23. package/src/runtime/enhanced-module-system.js +196 -0
  24. package/src/runtime/environment-enhanced.js +272 -0
  25. package/src/runtime/environment.js +140 -0
  26. package/src/runtime/event-emitter.js +232 -0
  27. package/src/runtime/formatter.js +280 -0
  28. package/src/runtime/functional.js +359 -0
  29. package/src/runtime/io-operations.js +390 -0
  30. package/src/runtime/linter.js +374 -0
  31. package/src/runtime/logging.js +314 -0
  32. package/src/runtime/minifier.js +242 -0
  33. package/src/runtime/module-system.js +377 -0
  34. package/src/runtime/network-operations.js +373 -0
  35. package/src/runtime/profiler.js +295 -0
  36. package/src/runtime/repl.js +336 -0
  37. package/src/runtime/security-manager.js +244 -0
  38. package/src/runtime/source-map-generator.js +208 -0
  39. package/src/runtime/test-runner.js +394 -0
  40. package/src/runtime/transformer.js +277 -0
  41. package/src/runtime/type-system.js +244 -0
  42. package/src/runtime/vladx-object.js +250 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Minifier — Минификатор кода VladX
3
+ */
4
+
5
+ export class Minifier {
6
+ constructor(options = {}) {
7
+ this.removeComments = options.removeComments !== false;
8
+ this.removeWhitespace = options.removeWhitespace !== false;
9
+ this.mangleNames = options.mangleNames || false;
10
+ this.renameVariables = options.renameVariables || false;
11
+ this.deadCodeElimination = options.deadCodeElimination || false;
12
+ }
13
+
14
+ /**
15
+ * Минифицировать код
16
+ */
17
+ minify(source, options = {}) {
18
+ let code = source;
19
+
20
+ // Удаление комментариев
21
+ if (this.removeComments) {
22
+ code = this.removeCommentsBlock(code);
23
+ }
24
+
25
+ // Удаление лишних пробелов
26
+ if (this.removeWhitespace) {
27
+ code = this.removeExtraWhitespace(code);
28
+ }
29
+
30
+ // Obfuscation имен переменных
31
+ if (this.mangleNames) {
32
+ code = this.mangleVariableNames(code);
33
+ }
34
+
35
+ // Удаление мертвого кода
36
+ if (this.deadCodeElimination) {
37
+ code = this.removeDeadCode(code);
38
+ }
39
+
40
+ return {
41
+ code,
42
+ originalSize: source.length,
43
+ minifiedSize: code.length,
44
+ reduction: ((1 - code.length / source.length) * 100).toFixed(2) + '%'
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Удалить комментарии
50
+ */
51
+ removeCommentsBlock(code) {
52
+ // Многострочные комментарии /* */
53
+ code = code.replace(/\/\*[\s\S]*?\*\//g, '');
54
+
55
+ // Однострочные комментарии //
56
+ code = code.replace(/\/\/.*$/gm, '');
57
+
58
+ // Русские комментарии #
59
+ code = code.replace(/#.*$/gm, '');
60
+
61
+ return code;
62
+ }
63
+
64
+ /**
65
+ * Удалить лишние пробелы
66
+ */
67
+ removeExtraWhitespace(code) {
68
+ // Заменить несколько пробелов одним
69
+ code = code.replace(/[ \t]+/g, ' ');
70
+
71
+ // Удалить пробелы до и после операторов
72
+ const operators = ['+', '-', '*', '/', '%', '=', '==', '!=', '<', '>', '<=', '>=', '&&', '||', '!', '&', '|', '^', '~'];
73
+ for (const op of operators) {
74
+ code = code.replace(new RegExp(`\\s*\\${op}\\s*`, 'g'), op);
75
+ }
76
+
77
+ // Удалить пробелы вокруг скобок и фигурных скобок
78
+ code = code.replace(/\s*([(){}[\],;:])\s*/g, '$1');
79
+
80
+ // Удалить пробелы в начале и конце строк
81
+ code = code.split('\n').map(line => line.trim()).join(' ');
82
+
83
+ // Удалить пустые строки
84
+ code = code.replace(/\n\s*\n/g, '\n');
85
+
86
+ return code.trim();
87
+ }
88
+
89
+ /**
90
+ * Obfuscation имен переменных
91
+ */
92
+ mangleVariableNames(code) {
93
+ const keywords = new Set([
94
+ 'пусть', 'константа', 'функция', 'класс', 'вернуть', 'если', 'иначе', 'пока', 'для',
95
+ 'импорт', 'экспорт', 'из', 'поумолчанию', 'истина', 'ложь', 'ничто',
96
+ 'async', 'await', 'this', 'super', 'new', 'typeof', 'instanceof'
97
+ ]);
98
+
99
+ const nameMap = new Map();
100
+ let nameIndex = 0;
101
+
102
+ const generateShortName = () => {
103
+ const chars = 'abcdefghijklmnopqrstuvwxyz';
104
+ let name = '';
105
+
106
+ do {
107
+ name = chars[nameIndex % chars.length] + (Math.floor(nameIndex / chars.length) > 0 ? Math.floor(nameIndex / chars.length) : '');
108
+ nameIndex++;
109
+ } while (keywords.has(name));
110
+
111
+ return name;
112
+ };
113
+
114
+ // Найти объявления переменных
115
+ const letPattern = /(?:пусть|let)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g;
116
+ const constPattern = /(?:константа|const)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g;
117
+ const funcPattern = /(?:функция|function)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g;
118
+ const paramPattern = /\(([^)]+)\)/g;
119
+
120
+ // Собрать все имена
121
+ const names = new Set();
122
+ let match;
123
+
124
+ while ((match = letPattern.exec(code)) !== null) {
125
+ names.add(match[1]);
126
+ }
127
+
128
+ while ((match = constPattern.exec(code)) !== null) {
129
+ names.add(match[1]);
130
+ }
131
+
132
+ while ((match = funcPattern.exec(code)) !== null) {
133
+ names.add(match[1]);
134
+ }
135
+
136
+ while ((match = paramPattern.exec(code)) !== null) {
137
+ const params = match[1].split(',').map(p => p.trim());
138
+ params.forEach(p => {
139
+ if (!keywords.has(p)) {
140
+ names.add(p);
141
+ }
142
+ });
143
+ }
144
+
145
+ // Создать map коротких имен
146
+ for (const name of names) {
147
+ if (!keywords.has(name) && !nameMap.has(name)) {
148
+ nameMap.set(name, generateShortName());
149
+ }
150
+ }
151
+
152
+ // Заменить имена в коде
153
+ let mangled = code;
154
+ for (const [oldName, newName] of nameMap) {
155
+ const regex = new RegExp(`\\b${oldName}\\b`, 'g');
156
+ mangled = mangled.replace(regex, newName);
157
+ }
158
+
159
+ return mangled;
160
+ }
161
+
162
+ /**
163
+ * Удаление мертвого кода
164
+ */
165
+ removeDeadCode(code) {
166
+ // Удалить недостижимый код после return
167
+ code = code.replace(/(?:вернуть|return)[^;{};]*;[\s\S]*?(?=[{}]|$)/g, match => {
168
+ const lines = match.split('\n');
169
+ return lines[0] + '\n';
170
+ });
171
+
172
+ // Удалить недостижимый код после throw
173
+ code = code.replace(/(?:выбросить|throw)[^;{};]*;[\s\S]*?(?=[{}]|$)/g, match => {
174
+ const lines = match.split('\n');
175
+ return lines[0] + '\n';
176
+ });
177
+
178
+ // Удалить пустые if блоки
179
+ code = code.replace(/(?:если|if)\s*\([^)]*\)\s*{\s*}/g, '');
180
+
181
+ // Удалить while(true) с break без тела
182
+ code = code.replace(/(?:пока|while)\s*\(истина\)\s*{\s*(?:прервать|break)\s*;}/g, '');
183
+
184
+ // Удалить бесполезные присваивания
185
+ code = code.replace(/([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\1\s*;?/g, '');
186
+
187
+ return code;
188
+ }
189
+
190
+ /**
191
+ * Обфускация строк
192
+ */
193
+ obfuscateStrings(code) {
194
+ // Превратить строки в escape sequences
195
+ return code.replace(/(["'])((?:\\.|(?!\1)[^\\])*)\1/g, (match, quote, content) => {
196
+ return JSON.stringify(content);
197
+ });
198
+ }
199
+
200
+ /**
201
+ * Сжать код с помощью base64 (для дополнительной обфускации)
202
+ */
203
+ encode(code) {
204
+ const encoded = Buffer.from(code).toString('base64');
205
+ return `eval(atob('${encoded}'))`;
206
+ }
207
+
208
+ /**
209
+ * Минификация с AST
210
+ */
211
+ minifyAST(ast) {
212
+ // Рекурсивно обходим AST и удаляем ненужные узлы
213
+ const optimize = (node) => {
214
+ if (!node) return null;
215
+
216
+ // Удалить пустые блоки
217
+ if (node.type === 'BlockStatement' && (!node.body || node.body.length === 0)) {
218
+ return null;
219
+ }
220
+
221
+ // Удалить недостижимые выражения
222
+ if (node.type === 'ExpressionStatement' && !node.expression) {
223
+ return null;
224
+ }
225
+
226
+ // Удалить ненужные свойства
227
+ for (const key in node) {
228
+ if (Array.isArray(node[key])) {
229
+ node[key] = node[key].map(optimize).filter(n => n !== null);
230
+ } else if (typeof node[key] === 'object') {
231
+ node[key] = optimize(node[key]);
232
+ }
233
+ }
234
+
235
+ return node;
236
+ };
237
+
238
+ return optimize(ast);
239
+ }
240
+ }
241
+
242
+ export default Minifier;
@@ -0,0 +1,377 @@
1
+ /**
2
+ * VladX Module System — Система модулей
3
+ * Управляет импортом и экспортом модулей, включая поддержку пакетов vladpm
4
+ */
5
+
6
+ import { readFileSync, existsSync } from 'fs';
7
+ import { dirname, join, extname, isAbsolute, basename } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { Lexer } from '../lexer/lexer.js';
10
+ import { Parser } from '../parser/parser.js';
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ export class ModuleSystem {
14
+ constructor(interpreter) {
15
+ this.interpreter = interpreter;
16
+ this.loadedModules = new Map();
17
+ this.moduleCache = new Map();
18
+
19
+ // Пути для поиска модулей (аналог Node.js modules paths)
20
+ this.nodeModulesPaths = [];
21
+ }
22
+
23
+ /**
24
+ * Установка путей для поиска модулей
25
+ */
26
+ setNodeModulesPaths(paths) {
27
+ this.nodeModulesPaths = paths;
28
+ }
29
+
30
+ /**
31
+ * Проверка, является ли путь пакетом (а не файлом)
32
+ */
33
+ isPackageName(modulePath) {
34
+ // Пакеты не начинаются с ./ или .. или /
35
+ // Могут быть @-scoped (@scope/package)
36
+ return !modulePath.startsWith('./') &&
37
+ !modulePath.startsWith('../') &&
38
+ !modulePath.startsWith('/') &&
39
+ modulePath.length > 0;
40
+ }
41
+
42
+ /**
43
+ * Разбор имени пакета
44
+ */
45
+ parsePackageName(name) {
46
+ // Проверка на scoped package (@scope/name)
47
+ const scopedMatch = name.match(/^@([^/]+)\/(.+)$/);
48
+ if (scopedMatch) {
49
+ return {
50
+ scope: scopedMatch[1],
51
+ name: scopedMatch[2],
52
+ fullName: name
53
+ };
54
+ }
55
+
56
+ // Обычный пакет
57
+ return {
58
+ scope: null,
59
+ name: name,
60
+ fullName: name
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Поиск модуля в node_modules по имени файла (без вложенной папки)
66
+ * Например: math.vx в node_modules/math.vx
67
+ */
68
+ findDirectModule(moduleName, currentPath) {
69
+ // Проверяем каждый путь node_modules для прямого файла
70
+ for (const basePath of this.nodeModulesPaths) {
71
+ // Пробуем расширение .vx
72
+ const vxPath = join(basePath, moduleName + '.vx');
73
+ if (existsSync(vxPath)) {
74
+ return vxPath;
75
+ }
76
+
77
+ // Пробуем .js
78
+ const jsPath = join(basePath, moduleName + '.js');
79
+ if (existsSync(jsPath)) {
80
+ return jsPath;
81
+ }
82
+
83
+ // Пробуем index.vx (node_modules/name/index.vx)
84
+ const indexPath = join(basePath, moduleName, 'index.vx');
85
+ if (existsSync(indexPath)) {
86
+ return indexPath;
87
+ }
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Поиск пакета в node_modules
95
+ */
96
+ findPackage(packageInfo, currentPath) {
97
+ // Сначала пробуем найти прямой файл (node_modules/math.vx)
98
+ const directModule = this.findDirectModule(packageInfo.name, currentPath);
99
+ if (directModule) {
100
+ return { path: directModule, isDirect: true };
101
+ }
102
+
103
+ // Пробуем локальные node_modules
104
+ for (const basePath of this.nodeModulesPaths) {
105
+ const packagePath = join(basePath, packageInfo.fullName);
106
+
107
+ if (existsSync(packagePath)) {
108
+ // Проверяем, это файл или директория
109
+ if (existsSync(join(packagePath, 'package.json'))) {
110
+ return { path: packagePath, isDirect: false }; // Пакет с package.json
111
+ }
112
+ if (existsSync(join(packagePath, 'index.vx'))) {
113
+ return { path: packagePath, isDirect: false }; // Пакет с index.vx
114
+ }
115
+ }
116
+
117
+ // Для scoped packages пробуем по-другому
118
+ if (packageInfo.scope) {
119
+ const scopedPath = join(basePath, '@' + packageInfo.scope, packageInfo.name);
120
+ if (existsSync(scopedPath)) {
121
+ return { path: scopedPath, isDirect: false };
122
+ }
123
+ }
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Получение главного файла пакета из package.json
131
+ */
132
+ getPackageMain(packagePath) {
133
+ // Если это не директория, а файл - возвращаем как есть
134
+ if (!existsSync(join(packagePath, 'package.json')) && !existsSync(join(packagePath, 'index.vx'))) {
135
+ if (existsSync(packagePath)) {
136
+ const stat = require('fs').statSync(packagePath);
137
+ if (stat.isFile()) {
138
+ return packagePath;
139
+ }
140
+ }
141
+ // Пробуем найти файл с расширением .vx в этой директории
142
+ const pkgName = basename(packagePath);
143
+ const vxFile = join(packagePath, pkgName + '.vx');
144
+ if (existsSync(vxFile)) {
145
+ return vxFile;
146
+ }
147
+ return packagePath;
148
+ }
149
+
150
+ const packageJsonPath = join(packagePath, 'package.json');
151
+
152
+ if (!existsSync(packageJsonPath)) {
153
+ // Нет package.json, пробуем index.vx
154
+ if (existsSync(join(packagePath, 'index.vx'))) {
155
+ return join(packagePath, 'index.vx');
156
+ }
157
+ throw new Error(`В пакете отсутствует package.json: ${packagePath}`);
158
+ }
159
+
160
+ try {
161
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
162
+
163
+ // Получаем main файл
164
+ let mainFile = packageJson.main || 'index.js';
165
+
166
+ // Пробуем .vx версию main файла
167
+ const vxMain = mainFile.replace(/\.js$/, '.vx');
168
+ if (existsSync(join(packagePath, vxMain))) {
169
+ return join(packagePath, vxMain);
170
+ }
171
+
172
+ // Пробуем index.vx
173
+ if (existsSync(join(packagePath, 'index.vx'))) {
174
+ return join(packagePath, 'index.vx');
175
+ }
176
+
177
+ return join(packagePath, mainFile);
178
+ } catch (error) {
179
+ const errorMessage = error.toString ? error.toString() : String(error) || 'Неизвестная ошибка';
180
+ throw new Error(`Ошибка чтения package.json: ${errorMessage}`);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Разрешение пути модуля (файл или пакет)
186
+ */
187
+ resolveModulePath(modulePath, currentPath) {
188
+ // Проверяем, является ли это именем пакета
189
+ if (this.isPackageName(modulePath)) {
190
+ return this.resolvePackagePath(modulePath, currentPath);
191
+ }
192
+
193
+ // Иначе это локальный файл
194
+ return this.resolveFilePath(modulePath, currentPath);
195
+ }
196
+
197
+ /**
198
+ * Разрешение пути к файлу модуля
199
+ */
200
+ resolveFilePath(modulePath, currentPath) {
201
+ // Извлекаем директорию из текущего пути
202
+ let currentDir = dirname(currentPath);
203
+
204
+ // Если путь не абсолютный, используем текущую рабочую директорию как базу
205
+ if (!isAbsolute(currentPath)) {
206
+ currentDir = process.cwd();
207
+ }
208
+
209
+ // Если передан не абсолютный путь, преобразуем относительно директории текущего файла
210
+ if (!isAbsolute(modulePath)) {
211
+ modulePath = join(currentDir, modulePath);
212
+ }
213
+
214
+ // Проверка расширений
215
+ const extensions = ['.vx', '.js', '.vladx', '.json'];
216
+
217
+ for (const ext of extensions) {
218
+ if (existsSync(modulePath + ext)) {
219
+ return modulePath + ext;
220
+ }
221
+ }
222
+
223
+ // Проверка файла без расширения
224
+ if (existsSync(modulePath)) {
225
+ return modulePath;
226
+ }
227
+
228
+ throw new Error(`Модуль "${modulePath}" не найден`);
229
+ }
230
+
231
+ /**
232
+ * Разрешение пути к пакету
233
+ */
234
+ resolvePackagePath(packagePath, currentPath) {
235
+ const packageInfo = this.parsePackageName(packagePath);
236
+
237
+ // Ищем пакет в node_modules
238
+ const foundResult = this.findPackage(packageInfo, currentPath);
239
+
240
+ if (!foundResult) {
241
+ throw new Error(`Пакет "${packageInfo.fullName}" не найден. Установите его с помощью: vladpm установить ${packageInfo.fullName}`);
242
+ }
243
+
244
+ // Если нашли прямой файл (node_modules/math.vx), возвращаем его сразу
245
+ if (foundResult.isDirect) {
246
+ return foundResult.path;
247
+ }
248
+
249
+ // Иначе это директория пакета, получаем главный файл
250
+ return this.getPackageMain(foundResult.path);
251
+ }
252
+
253
+ /**
254
+ * Загрузка модуля
255
+ */
256
+ async loadModule(modulePath, currentPath) {
257
+
258
+ // Разрешаем путь модуля
259
+ const resolvedPath = this.resolveModulePath(modulePath, currentPath);
260
+
261
+ // Проверка кэша
262
+ if (this.moduleCache.has(resolvedPath)) {
263
+ return this.moduleCache.get(resolvedPath);
264
+ }
265
+
266
+ // Проверка циклической зависимости
267
+ if (this.loadedModules.has(resolvedPath)) {
268
+ return this.loadedModules.get(resolvedPath);
269
+ }
270
+
271
+ // Чтение файла
272
+ if (!existsSync(resolvedPath)) {
273
+ throw new Error(`Файл модуля не найден: ${resolvedPath}`);
274
+ }
275
+
276
+ const source = readFileSync(resolvedPath, 'utf-8');
277
+ const moduleDir = dirname(resolvedPath);
278
+
279
+ // Создание нового окружения для модуля
280
+ const moduleEnv = this.interpreter.globalEnv.child(`<module:${resolvedPath}>`);
281
+ moduleEnv.exports = {};
282
+
283
+ // Парсинг и выполнение с новым окружением
284
+ try {
285
+ // Создаём временный интерпретатор для модуля
286
+ // Создаём временный интерпретатор для модуля (с абсолютными путями)
287
+
288
+
289
+ const lexer = new Lexer(source, resolvedPath);
290
+ const tokens = lexer.tokenize();
291
+ const parser = new Parser(tokens);
292
+ const ast = parser.parse();
293
+
294
+ // Временно меняем окружение
295
+ const oldEnv = this.interpreter.currentEnv;
296
+ this.interpreter.currentEnv = moduleEnv;
297
+
298
+ // Добавляем путь node_modules для вложенных импортов
299
+ const oldPaths = this.nodeModulesPaths;
300
+ this.nodeModulesPaths = [moduleDir, ...this.nodeModulesPaths];
301
+
302
+ // Выполняем модуль
303
+ // Выполняем модуль
304
+ // Регистрируем встроенные функции в окружении модуля
305
+ this.interpreter.registerBuiltins();
306
+ try {
307
+ await this.interpreter.interpret(ast, {
308
+ filename: resolvedPath,
309
+ environment: moduleEnv
310
+ });
311
+
312
+ } catch (moduleError) {
313
+ console.error('[ModuleSystem] ОШИБКА ВЫПОЛНЕНИЯ МОДУЛЯ:', moduleError);
314
+ console.error('[ModuleSystem] Стек:', moduleError.stack);
315
+ throw moduleError;
316
+ }
317
+ // Восстанавливаем пути
318
+ this.nodeModulesPaths = oldPaths;
319
+
320
+ // Восстанавливаем окружение
321
+ this.interpreter.currentEnv = oldEnv;
322
+
323
+ // Извлекаем экспорты
324
+ const moduleExports = moduleEnv.exports && Object.keys(moduleEnv.exports).length > 0
325
+ ? moduleEnv.exports
326
+ : this.extractAllFromEnv(moduleEnv);
327
+
328
+ // Кэширование модуля
329
+ this.moduleCache.set(resolvedPath, moduleExports);
330
+ this.loadedModules.set(resolvedPath, moduleExports);
331
+
332
+ return moduleExports;
333
+ } catch (error) {
334
+ let errorMessage = error.toString ? error.toString() : String(error);
335
+ if (!errorMessage) {
336
+ errorMessage = `Ошибка типа: ${error.constructor.name}`;
337
+ if (error.stack) {
338
+ errorMessage += `\nСтек вызовов: ${error.stack}`;
339
+ } else {
340
+ errorMessage += ' (Стек вызовов недоступен)';
341
+ }
342
+ }
343
+ throw new Error(`Ошибка загрузки модуля ${resolvedPath}: ${errorMessage}`);
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Извлечение всех значений из окружения
349
+ */
350
+ extractAllFromEnv(env) {
351
+ const exports = {};
352
+
353
+ // Добавляем экспорты, если они есть
354
+ if (env.exports) {
355
+ Object.assign(exports, env.exports);
356
+ }
357
+
358
+ // Добавляем все переменные, если их нет в экспортах
359
+ for (const [key, value] of env.variables) {
360
+ if (!(key in exports)) {
361
+ exports[key] = value;
362
+ }
363
+ }
364
+
365
+ return exports;
366
+ }
367
+
368
+ /**
369
+ * Очистка кэша модулей
370
+ */
371
+ clearCache() {
372
+ this.loadedModules.clear();
373
+ this.moduleCache.clear();
374
+ }
375
+ }
376
+
377
+ export default ModuleSystem;