@mbler/mcx-core 0.0.2-alpha

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RuanhoR
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # mcx
2
+
File without changes
@@ -0,0 +1,1039 @@
1
+ 'use strict';
2
+
3
+ var fs = require('node:fs/promises');
4
+ var path = require('node:path');
5
+ var t = require('@babel/types');
6
+ var Parser = require('@babel/parser');
7
+
8
+ function _interopNamespaceDefault(e) {
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
26
+ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
27
+ var t__namespace = /*#__PURE__*/_interopNamespaceDefault(t);
28
+ var Parser__namespace = /*#__PURE__*/_interopNamespaceDefault(Parser);
29
+
30
+ let Lexer$1 = class Lexer {
31
+ text;
32
+ booleanProxyCache;
33
+ constructor(text) {
34
+ this.text = text;
35
+ this.booleanProxyCache = new WeakMap();
36
+ }
37
+ get tokens() {
38
+ return {
39
+ [Symbol.iterator]: () => this.tokenIterator()
40
+ };
41
+ }
42
+ /**
43
+ * 解析标签属性,如:<div id="app" disabled />
44
+ */
45
+ parseAttributes(tagContent) {
46
+ const attributes = {};
47
+ let currentKey = '';
48
+ let currentValue = '';
49
+ let inKey = true;
50
+ let name = '';
51
+ let inValue = false;
52
+ let quoteChar = null;
53
+ let isTagName = true;
54
+ for (let i = 0; i < tagContent.length; i++) {
55
+ const char = tagContent[i];
56
+ if (isTagName) {
57
+ if (char === ' ' || char === '>') {
58
+ name = currentKey.trim();
59
+ currentKey = '';
60
+ isTagName = false;
61
+ if (char === '>')
62
+ break;
63
+ }
64
+ else {
65
+ currentKey += char;
66
+ }
67
+ continue;
68
+ }
69
+ if (inValue) {
70
+ if (char === quoteChar &&
71
+ (currentValue.length === 0 || currentValue[currentValue.length - 1] !== '\\')) {
72
+ attributes[currentKey.trim()] = currentValue;
73
+ currentKey = '';
74
+ currentValue = '';
75
+ inKey = true;
76
+ inValue = false;
77
+ quoteChar = null;
78
+ }
79
+ else {
80
+ currentValue += char;
81
+ }
82
+ }
83
+ else if (char === '=' && inKey) {
84
+ inKey = false;
85
+ inValue = true;
86
+ const nextIndex = i + 1;
87
+ const nextChar = nextIndex < tagContent.length ? tagContent[nextIndex] : ' ';
88
+ quoteChar = (nextChar === '"' || nextChar === "'") ? nextChar : null;
89
+ }
90
+ else if (char === ' ' && inKey && currentKey) {
91
+ attributes[currentKey.trim()] = 'true';
92
+ currentKey = '';
93
+ }
94
+ else if (inKey) {
95
+ currentKey += char;
96
+ }
97
+ }
98
+ if (isTagName) {
99
+ name = currentKey.trim();
100
+ }
101
+ else if (currentKey) {
102
+ attributes[currentKey.trim()] = inValue ?
103
+ currentValue.replace(/^["']/, '').replace(/["']$/, '') :
104
+ 'true';
105
+ }
106
+ return {
107
+ name,
108
+ arr: attributes,
109
+ };
110
+ }
111
+ /**
112
+ * 拆分输入文本为 Token 流:Tag、TagEnd、Content
113
+ * 新增:忽略 HTML 注释 <!-- ... --> 并记录每个 token 的起始位置与行号
114
+ */
115
+ *tagSplitIterator() {
116
+ const text = this.text;
117
+ let i = 0;
118
+ let line = 1;
119
+ const len = text.length;
120
+ while (i < len) {
121
+ const ch = text[i];
122
+ if (ch === '<') {
123
+ // 检查注释 <!-- ... -->
124
+ if (text.startsWith('!--', i + 1)) {
125
+ const endIdx = text.indexOf('-->', i + 4);
126
+ const commentEnd = endIdx === -1 ? len - 1 : endIdx + 2;
127
+ // 更新行号
128
+ const segment = text.slice(i, commentEnd + 1);
129
+ for (const c of segment)
130
+ if (c === '\n')
131
+ line++;
132
+ i = commentEnd + 1;
133
+ continue; // 跳过注释
134
+ }
135
+ // 普通标签读取到 '>'
136
+ const tokenStart = i;
137
+ const tokenStartLine = line;
138
+ let j = i + 1;
139
+ let sawGt = false;
140
+ for (; j < len; j++) {
141
+ const c = text[j];
142
+ if (c === '>') {
143
+ sawGt = true;
144
+ break;
145
+ }
146
+ if (c === '\n')
147
+ line++;
148
+ }
149
+ const tokenEnd = j;
150
+ const buffer = text.slice(tokenStart, sawGt ? j + 1 : len);
151
+ const type = buffer.startsWith('</') ? 'TagEnd' : 'Tag';
152
+ const tok = {
153
+ data: buffer,
154
+ type,
155
+ startIndex: tokenStart,
156
+ endIndex: sawGt ? tokenEnd : len - 1,
157
+ startLine: tokenStartLine
158
+ };
159
+ yield tok;
160
+ i = sawGt ? j + 1 : len;
161
+ }
162
+ else {
163
+ // 内容直到下一个 '<'
164
+ const contentStart = i;
165
+ const contentStartLine = line;
166
+ let j = i;
167
+ for (; j < len; j++) {
168
+ const c = text[j];
169
+ if (c === '<')
170
+ break;
171
+ if (c === '\n')
172
+ line++;
173
+ }
174
+ const data = text.slice(contentStart, j);
175
+ const n = {
176
+ data,
177
+ type: 'Content',
178
+ startIndex: contentStart,
179
+ endIndex: j - 1,
180
+ startLine: contentStartLine
181
+ };
182
+ yield n;
183
+ i = j;
184
+ }
185
+ }
186
+ }
187
+ /**
188
+ * 生成 Token 迭代器,用于遍历所有结构化 Token
189
+ * 改为基于 stack 的解析以支持嵌套,并为 ParsedTagNode 添加 loc: { start:{line,index}, end:{line,index} }
190
+ * Content 改为递归节点数组 (ParsedTagContentNode | ParsedTagNode)[]
191
+ */
192
+ *tokenIterator() {
193
+ const rawTokens = Array.from(this.tagSplitIterator());
194
+ const stack = [];
195
+ for (let idx = 0; idx < rawTokens.length; idx++) {
196
+ const token = rawTokens[idx];
197
+ if (!token)
198
+ continue;
199
+ if (token.type === 'Content') {
200
+ const contentNode = {
201
+ data: token.data,
202
+ type: 'TagContent'
203
+ };
204
+ if (stack.length > 0) {
205
+ const top = stack[stack.length - 1];
206
+ top.content.push(contentNode);
207
+ }
208
+ }
209
+ else if (token.type === 'Tag') {
210
+ const inner = token.data.slice(1, -1).trim();
211
+ // 自闭合 <br/> 或 <img ... /> 也当作单节点(没有 end),这里简单检测末尾 '/'
212
+ const isSelfClosing = inner.endsWith('/');
213
+ const arr = this.parseAttributes(isSelfClosing ? inner.slice(0, -1).trim() : inner);
214
+ const node = {
215
+ start: token,
216
+ name: arr.name,
217
+ arr: arr.arr,
218
+ // content 现在是一个数组,包含文本节点或子标签
219
+ content: [],
220
+ end: null,
221
+ // loc: start/end positions will be set when available
222
+ loc: {
223
+ start: { line: token.startLine || 1, index: token.startIndex || 0 },
224
+ end: { line: token.startLine || 1, index: token.endIndex || (token.startIndex || 0) }
225
+ }
226
+ };
227
+ if (isSelfClosing) {
228
+ // self-closing: immediately close and attach to parent or root
229
+ if (stack.length > 0) {
230
+ stack[stack.length - 1].content.push(node);
231
+ }
232
+ else {
233
+ // yield top-level node
234
+ yield node;
235
+ }
236
+ }
237
+ else {
238
+ stack.push(node);
239
+ }
240
+ }
241
+ else if (token.type === 'TagEnd') {
242
+ // 从 '</name>' 中提取 name
243
+ const name = token.data.replace(/^<\/\s*/, '').replace(/\s*>$/, '').trim();
244
+ // 找到最近的匹配开始标签
245
+ for (let s = stack.length - 1; s >= 0; s--) {
246
+ const candidate = stack[s];
247
+ if (candidate && candidate.name === name) {
248
+ // 设置结束
249
+ candidate.end = token;
250
+ candidate.loc.end = { line: token.startLine || candidate.loc.start.line, index: token.endIndex || (token.loc.start.index) };
251
+ // 从 stack 中移除并附加到父节点或作为顶层节点产出
252
+ stack.splice(s, 1);
253
+ if (stack.length > 0) {
254
+ stack[stack.length - 1].content.push(candidate);
255
+ }
256
+ else {
257
+ // yield completed top-level node
258
+ yield candidate;
259
+ }
260
+ break;
261
+ }
262
+ }
263
+ // 如果没有匹配的开始标签,则忽略(或可扩展为错误处理)
264
+ }
265
+ }
266
+ // 如果有未闭合的标签,则把它们作为顶层节点输出(保留当前内容)
267
+ while (stack.length > 0) {
268
+ const node = stack.shift();
269
+ if (stack.length > 0) {
270
+ stack[0].content.push(node);
271
+ }
272
+ else {
273
+ yield node;
274
+ }
275
+ }
276
+ }
277
+ /**
278
+ * 创建一个动态布尔属性访问的 Proxy(可选功能)
279
+ */
280
+ getBooleanCheckProxy() {
281
+ if (!this.booleanProxyCache.has(this)) {
282
+ const charMap = new Map();
283
+ const proxy = new Proxy({}, {
284
+ get(_, prop) {
285
+ if (typeof prop !== 'string')
286
+ return false;
287
+ return charMap.get(prop) || false;
288
+ },
289
+ set(_, prop, value) {
290
+ if (typeof prop !== 'string')
291
+ return false;
292
+ charMap.set(prop, Boolean(value));
293
+ return true;
294
+ },
295
+ });
296
+ this.booleanProxyCache.set(this, proxy);
297
+ }
298
+ return this.booleanProxyCache.get(this);
299
+ }
300
+ };
301
+ class McxAst {
302
+ text;
303
+ constructor(text) {
304
+ this.text = text;
305
+ }
306
+ getAST() {
307
+ const lexer = new Lexer$1(this.text);
308
+ // 现在 tokenIterator 直接产生顶层 ParsedTagNode(并且注释已被忽略)
309
+ return Array.from(lexer.tokens);
310
+ }
311
+ get data() {
312
+ return this.getAST();
313
+ }
314
+ parseAST() {
315
+ return this.getAST();
316
+ }
317
+ /**
318
+ * 生成代码字符串(递归处理 content 数组)
319
+ * @param node 要生成代码的AST节点
320
+ * @returns 生成的代码字符串
321
+ */
322
+ static generateCode(node) {
323
+ let code = `<${node.name}`;
324
+ // 添加属性
325
+ for (const [key, value] of Object.entries(node.arr || {})) {
326
+ if (value === 'true') {
327
+ code += ` ${key}`;
328
+ }
329
+ else {
330
+ code += ` ${key}=${String(value)}`;
331
+ }
332
+ }
333
+ code += '>';
334
+ // 添加内容(content 现在为数组)
335
+ const contentArr = node.content;
336
+ if (Array.isArray(contentArr)) {
337
+ for (const item of contentArr) {
338
+ if (item.type === 'TagContent') {
339
+ code += item.data;
340
+ }
341
+ else {
342
+ code += McxAst.generateCode(item);
343
+ }
344
+ }
345
+ }
346
+ // 添加结束标签
347
+ code += `</${node.name}>`;
348
+ return code;
349
+ }
350
+ }
351
+
352
+ const STATUS = [0, 1]; // 0: 搜集 key,1: 搜集 value
353
+ class Lexer {
354
+ code;
355
+ constructor(code) {
356
+ this.code = code;
357
+ }
358
+ // 对外暴露的 tokenize 方法(生成器函数)
359
+ *tokenize() {
360
+ let currStatus = STATUS[0]; // 0: key,1: value
361
+ let key = "";
362
+ let value = "";
363
+ for (const char of this.code) {
364
+ if (/\s/.test(char)) {
365
+ if (char === '\n') {
366
+ if (currStatus === STATUS[1] && key && value) {
367
+ const propNode = {
368
+ key,
369
+ value: this.HandlerValue(value),
370
+ type: "PropChar"
371
+ };
372
+ yield propNode;
373
+ }
374
+ key = "";
375
+ value = "";
376
+ currStatus = STATUS[0];
377
+ }
378
+ continue; // 跳过所有空白字符
379
+ }
380
+ if (char === '=') {
381
+ if (currStatus === STATUS[0]) {
382
+ currStatus = STATUS[1]; // 切换到 value 状态
383
+ }
384
+ }
385
+ else {
386
+ if (currStatus === STATUS[0]) {
387
+ key += char; // 搜集 key
388
+ }
389
+ else if (currStatus === STATUS[1]) {
390
+ value += char; // 搜集 value
391
+ }
392
+ }
393
+ }
394
+ if (key && value) {
395
+ const propNode = {
396
+ key,
397
+ value: this.HandlerValue(value),
398
+ type: "PropChar"
399
+ };
400
+ yield propNode;
401
+ }
402
+ }
403
+ HandlerValue(value) {
404
+ try {
405
+ const num = Number(value);
406
+ if (!Number.isNaN(num))
407
+ return num;
408
+ if (["[", "{"].includes(value.slice(0, 1)) && ["]", "}"].includes(value.slice(-1))) {
409
+ return JSON.parse(value);
410
+ }
411
+ }
412
+ catch { }
413
+ return value;
414
+ }
415
+ }
416
+ // 默认导出解析函数
417
+ function PropParser(code) {
418
+ const lexer = new Lexer(code);
419
+ return Array.from(lexer.tokenize());
420
+ }
421
+
422
+ var AST = {
423
+ tag: McxAst,
424
+ prop: PropParser
425
+ };
426
+
427
+ class McxUtlis {
428
+ /**
429
+ * 检查文件是否存在
430
+ * @param path 文件路径
431
+ * @returns 是否存在
432
+ */
433
+ static async FileExsit(path) {
434
+ try {
435
+ await fs__namespace.access(path);
436
+ return true;
437
+ }
438
+ catch {
439
+ return false;
440
+ }
441
+ }
442
+ /**
443
+ * 读取文件内容,支持返回 string 或 object,带重试机制
444
+ * @param filePath 文件路径
445
+ * @param opt 配置选项
446
+ * @returns 文件内容(string | object),出错时返回 {}
447
+ */
448
+ static async readFile(filePath, opt = {}) {
449
+ // 补全必填字段,确保类型安全
450
+ const opts = {
451
+ delay: 200, // 默认延迟
452
+ maxRetries: 3, // 默认最大重试次数
453
+ want: 'string', // 默认返回字符串
454
+ ...opt, // 用户传入的配置覆盖默认值
455
+ };
456
+ for (let attempt = 0; attempt < opts.maxRetries; attempt++) {
457
+ try {
458
+ const buffer = await fs__namespace.readFile(filePath);
459
+ let text;
460
+ if (opts.want === 'string') {
461
+ text = buffer.toString(); // Buffer -> string
462
+ }
463
+ else if (opts.want === 'object') {
464
+ try {
465
+ text = JSON.parse(buffer.toString()); // Buffer -> string -> object
466
+ }
467
+ catch (parseErr) {
468
+ // JSON 解析失败时返回空对象
469
+ text = {};
470
+ }
471
+ }
472
+ else {
473
+ // 默认情况也返回字符串
474
+ text = buffer.toString();
475
+ }
476
+ return text;
477
+ }
478
+ catch (err) {
479
+ // 如果不是最后一次尝试,则等待后重试
480
+ if (attempt < opts.maxRetries - 1) {
481
+ await McxUtlis.sleep(opts.delay);
482
+ }
483
+ }
484
+ }
485
+ return opts.want === 'object' ? {} : '';
486
+ }
487
+ static sleep(time) {
488
+ return new Promise((resolve) => setTimeout(resolve, time));
489
+ }
490
+ // 在运行时进行对象类型验证
491
+ static TypeVerify(obj, types) {
492
+ for (const item of Object.entries(types)) {
493
+ const [key, ShouldType] = item;
494
+ if (!(typeof obj[key] === ShouldType))
495
+ return false;
496
+ }
497
+ return true;
498
+ }
499
+ static AbsoluteJoin(baseDir, inputPath) {
500
+ return path__namespace.isAbsolute(inputPath) ?
501
+ inputPath :
502
+ path__namespace.join(baseDir, inputPath);
503
+ }
504
+ }
505
+
506
+ class JsCompileData {
507
+ node;
508
+ BuildCache;
509
+ File = "__repl";
510
+ isFile = false;
511
+ constructor(node, BuildCache = {
512
+ export: [],
513
+ import: [],
514
+ call: []
515
+ }) {
516
+ this.node = node;
517
+ this.BuildCache = BuildCache;
518
+ }
519
+ setFilePath(dir) {
520
+ this.isFile = true;
521
+ this.File = dir;
522
+ }
523
+ }
524
+
525
+ class Utils {
526
+ static async FileAST(fileDir, parserOpt) {
527
+ if (typeof fileDir !== "string")
528
+ throw new TypeError("[read file]: compile utils was passed a non-string value");
529
+ const file = await fs.readFile(fileDir, "utf-8");
530
+ if (typeof file !== "string")
531
+ throw new Error("[read file]: not found file " + fileDir);
532
+ try {
533
+ return Parser__namespace.parse(file).program;
534
+ }
535
+ catch (err) {
536
+ throw new Error("[compile ast]: " + err.stack);
537
+ }
538
+ }
539
+ static async FileContent(fileDir) {
540
+ const file = await fs.readFile(fileDir, "utf-8");
541
+ return file;
542
+ }
543
+ static nodeStringValue(node) {
544
+ if (node.type == "StringLiteral") {
545
+ return node.value;
546
+ }
547
+ else if (node.type == "Identifier") {
548
+ return node.name;
549
+ }
550
+ throw new TypeError("[read id error]: no way to read string id");
551
+ }
552
+ static CheckImportNode(node, ir) {
553
+ const newList = Utils.ImportToCache(node);
554
+ // Eliminate common differences
555
+ if (newList.source !== ir.source)
556
+ return false;
557
+ if (newList.imported.length !== ir.imported.length)
558
+ return false;
559
+ // in this for, newList.imported and ir.imported is same length
560
+ for (let irIndex = 0; irIndex < newList.imported.length; irIndex++) {
561
+ const newItem = newList.imported[irIndex];
562
+ const oldItem = ir.imported[irIndex];
563
+ if (newItem?.import !== oldItem?.import || newItem?.as !== oldItem?.as || newItem?.isAll !== oldItem?.isAll)
564
+ return false;
565
+ }
566
+ return true;
567
+ }
568
+ static CacheToImportNode(ir) {
569
+ if (!ir)
570
+ throw new TypeError("plase call use right ImportList");
571
+ // first verify ir.raw
572
+ if (ir?.raw && Utils.CheckImportNode(ir?.raw, ir))
573
+ return ir.raw;
574
+ let result = [];
575
+ for (let ImportIt of ir.imported) {
576
+ if (!ImportIt)
577
+ continue;
578
+ if (ImportIt.isAll) {
579
+ result.push(t__namespace.importNamespaceSpecifier(t__namespace.identifier(ImportIt.as)));
580
+ continue;
581
+ }
582
+ if (ImportIt.import == "default") {
583
+ result.push(t__namespace.importDefaultSpecifier(t__namespace.identifier(ImportIt.as)));
584
+ continue;
585
+ }
586
+ if (!ImportIt.import)
587
+ throw new TypeError("[compile node]: not found imported");
588
+ result.push(t__namespace.importSpecifier(t__namespace.identifier(ImportIt.as), t__namespace.identifier(ImportIt.import)));
589
+ }
590
+ return t__namespace.importDeclaration(result, t__namespace.stringLiteral(ir.source));
591
+ }
592
+ static ImportToCache(node) {
593
+ const result = [];
594
+ for (let item of node.specifiers) {
595
+ const thisName = item.local.name;
596
+ if (item.type == "ImportNamespaceSpecifier") {
597
+ result.push({
598
+ isAll: true,
599
+ as: thisName
600
+ });
601
+ }
602
+ else if (item.type == "ImportDefaultSpecifier") {
603
+ result.push({
604
+ isAll: false,
605
+ import: "default",
606
+ as: thisName
607
+ });
608
+ }
609
+ else if (item.type == "ImportSpecifier") {
610
+ result.push({
611
+ isAll: false,
612
+ as: thisName,
613
+ import: Utils.nodeStringValue(item.imported)
614
+ });
615
+ }
616
+ }
617
+ return {
618
+ source: Utils.nodeStringValue(node.source),
619
+ imported: result
620
+ };
621
+ }
622
+ }
623
+
624
+ class CompileJS {
625
+ node;
626
+ constructor(node) {
627
+ this.node = node;
628
+ if (!t__namespace.isProgram(node))
629
+ throw new Error("[compile error]: jsCompile can't work in a not program");
630
+ this.CompileData = new JsCompileData(node);
631
+ this.run();
632
+ this.writeBuildCache();
633
+ }
634
+ TopContext = {};
635
+ indexTemp = {};
636
+ push(source) {
637
+ for (const node of source.imported) {
638
+ this.indexTemp[node.as] = {
639
+ source: source.source,
640
+ import: node.import,
641
+ isAll: node.isAll,
642
+ };
643
+ }
644
+ }
645
+ takeInnerMost(node) {
646
+ if (!t__namespace.isMemberExpression(node))
647
+ throw new Error("[take item}: must MemberExpression");
648
+ let current = node;
649
+ while (true) {
650
+ if (t__namespace.isMemberExpression(current)) {
651
+ current = current.object;
652
+ continue;
653
+ }
654
+ if (t__namespace.isCallExpression(current)) {
655
+ const callee = current.callee;
656
+ if (t__namespace.isMemberExpression(callee)) {
657
+ current = callee.object;
658
+ continue;
659
+ }
660
+ current = callee;
661
+ continue;
662
+ }
663
+ if (t__namespace.isIdentifier(current) ||
664
+ t__namespace.isThisExpression(current) ||
665
+ t__namespace.isSuper(current) ||
666
+ t__namespace.isImport(current) ||
667
+ t__namespace.isNewExpression(current) ||
668
+ (typeof t__namespace.isLiteral === "function" && t__namespace.isLiteral(current))) {
669
+ return current;
670
+ }
671
+ if (t__namespace.isLiteral(current)) {
672
+ return current;
673
+ }
674
+ return t__namespace.stringLiteral("");
675
+ }
676
+ }
677
+ writeImportKeys = [];
678
+ extractIdentifierNames(node) {
679
+ const identifiers = [];
680
+ if (t__namespace.isIdentifier(node)) {
681
+ identifiers.push(node.name);
682
+ }
683
+ else if (t__namespace.isMemberExpression(node)) {
684
+ identifiers.push(...this.extractIdentifierNames(node.object));
685
+ if (node.property.type !== "PrivateName") {
686
+ identifiers.push(...this.extractIdentifierNames(node.property));
687
+ }
688
+ }
689
+ else if (t__namespace.isCallExpression(node)) {
690
+ identifiers.push(...this.extractIdentifierNames(node.callee));
691
+ for (const arg of node.arguments) {
692
+ if (t__namespace.isExpression(arg)) {
693
+ identifiers.push(...this.extractIdentifierNames(arg));
694
+ }
695
+ }
696
+ }
697
+ else if (t__namespace.isBinaryExpression(node) || t__namespace.isLogicalExpression(node)) {
698
+ identifiers.push(...this.extractIdentifierNames(node.left));
699
+ identifiers.push(...this.extractIdentifierNames(node.right));
700
+ }
701
+ else if (t__namespace.isUnaryExpression(node)) {
702
+ identifiers.push(...this.extractIdentifierNames(node.argument));
703
+ }
704
+ else if (t__namespace.isConditionalExpression(node)) {
705
+ identifiers.push(...this.extractIdentifierNames(node.test));
706
+ identifiers.push(...this.extractIdentifierNames(node.consequent));
707
+ identifiers.push(...this.extractIdentifierNames(node.alternate));
708
+ }
709
+ return identifiers;
710
+ }
711
+ writeBuildCache() {
712
+ const currenySource = [];
713
+ let build = [];
714
+ for (const [as, data] of Object.entries(this.indexTemp)) {
715
+ // only include imports that were actually referenced
716
+ if (!this.writeImportKeys.includes(as))
717
+ continue;
718
+ if (currenySource.includes(data.source)) {
719
+ let isFound = false;
720
+ for (const index in build) {
721
+ const i = build[index];
722
+ if (!i)
723
+ continue;
724
+ if (i.source == data.source) {
725
+ i.imported.push({
726
+ as: as,
727
+ isAll: data.isAll,
728
+ import: data.import,
729
+ });
730
+ isFound = true;
731
+ }
732
+ }
733
+ if (!isFound)
734
+ throw new Error("[mcx compoiler]: internal error: unexpected source");
735
+ }
736
+ else {
737
+ build.push({
738
+ source: data.source,
739
+ imported: [
740
+ {
741
+ as,
742
+ import: data.import,
743
+ isAll: data.isAll,
744
+ },
745
+ ],
746
+ });
747
+ currenySource.push(data.source);
748
+ }
749
+ }
750
+ // write filtered imports into CompileData.BuildCache
751
+ this.CompileData.BuildCache.import = build;
752
+ }
753
+ CompileData;
754
+ getCompileData() {
755
+ return this.CompileData;
756
+ }
757
+ conditionalInTempImport(node, thisContext, remove) {
758
+ // If identifier, mark it
759
+ if (t__namespace.isIdentifier(node)) {
760
+ if (node.name in this.indexTemp &&
761
+ !this.writeImportKeys.includes(node.name)) {
762
+ this.writeImportKeys.push(node.name);
763
+ }
764
+ return;
765
+ }
766
+ if (node.type == "FunctionExpression") {
767
+ this.tre(t__namespace.blockStatement([node.body]));
768
+ return;
769
+ }
770
+ if (node.type == "ArrowFunctionExpression") {
771
+ if (t__namespace.isExpression(node.body)) {
772
+ this.conditionalInTempImport(node.body, thisContext, remove);
773
+ }
774
+ else {
775
+ this.tre(node.body);
776
+ }
777
+ return;
778
+ }
779
+ if (t__namespace.isLiteral(node))
780
+ return;
781
+ // If member expression, collect identifiers inside it
782
+ if (t__namespace.isMemberExpression(node)) {
783
+ const names = this.extractIdentifierNames(node);
784
+ for (const n of names) {
785
+ if (n in this.indexTemp && !this.writeImportKeys.includes(n))
786
+ this.writeImportKeys.push(n);
787
+ }
788
+ // recurse into object and property when applicable
789
+ this.conditionalInTempImport(node.object, thisContext, remove);
790
+ if (t__namespace.isExpression(node.property))
791
+ this.conditionalInTempImport(node.property, thisContext, remove);
792
+ return;
793
+ }
794
+ // Call expression: record call for buildcache and mark identifiers used in callee and args
795
+ if (t__namespace.isCallExpression(node) &&
796
+ node.callee?.type !== "V8IntrinsicIdentifier") {
797
+ this.CompileData.BuildCache.call.push({
798
+ source: node.callee,
799
+ arguments: node.arguments,
800
+ remove,
801
+ });
802
+ this.conditionalInTempImport(node.callee, thisContext, remove);
803
+ for (const arg of node.arguments) {
804
+ if (t__namespace.isExpression(arg))
805
+ this.conditionalInTempImport(arg, thisContext, remove);
806
+ }
807
+ return;
808
+ }
809
+ // Generic expressions: try to extract identifier names and mark them
810
+ try {
811
+ const names = this.extractIdentifierNames(node);
812
+ for (const n of names) {
813
+ if (n in this.indexTemp && !this.writeImportKeys.includes(n))
814
+ this.writeImportKeys.push(n);
815
+ }
816
+ }
817
+ catch (_) {
818
+ // ignore
819
+ }
820
+ }
821
+ tre(node, ExtendContext = {}) {
822
+ if (!t__namespace.isBlock(node))
823
+ throw new Error("[compile error]: can't for in not block node");
824
+ const isTop = t__namespace.isProgram(node);
825
+ const currenyContext = isTop ? this.TopContext : ExtendContext;
826
+ for (let index = 0; index < node.body.length; index++) {
827
+ const item = node.body[index];
828
+ const remove = () => {
829
+ node.body.splice(index, 1);
830
+ index--;
831
+ };
832
+ if (!item)
833
+ continue;
834
+ if (item.type == "ImportDeclaration") {
835
+ if (!isTop)
836
+ throw new Error("[compile node]: import declaration must use in top.");
837
+ this.push(Utils.ImportToCache(item));
838
+ remove();
839
+ }
840
+ else if (item.type == "BlockStatement") {
841
+ this.tre(item, currenyContext);
842
+ }
843
+ else if (item.type == "BreakStatement" ||
844
+ item.type == "EmptyStatement" ||
845
+ item.type == "ContinueStatement" ||
846
+ item.type == "ThrowStatement" ||
847
+ item.type == "WithStatement") {
848
+ continue;
849
+ }
850
+ else if (item.type == "TryStatement") {
851
+ this.tre(item.block, currenyContext);
852
+ }
853
+ else if (item.type == "IfStatement") {
854
+ const If = item.test;
855
+ this.conditionalInTempImport(If, currenyContext, remove);
856
+ const nodes = [item.consequent];
857
+ if (item.alternate)
858
+ nodes.push(item.alternate);
859
+ // if ... else ... make one by one
860
+ this.tre(t__namespace.blockStatement(nodes), currenyContext);
861
+ }
862
+ else if (item.type == "WhileStatement") {
863
+ this.tre(t__namespace.blockStatement([item.body]), currenyContext);
864
+ }
865
+ else if (item.type == "ClassDeclaration") {
866
+ if (item.superClass) {
867
+ const superClass = item.superClass;
868
+ let superId = null;
869
+ if (superClass.type == "Identifier") {
870
+ superId = superClass.name;
871
+ }
872
+ if (superClass.type == "MemberExpression") {
873
+ // take the innermost item
874
+ const tempNode = this.takeInnerMost(superClass);
875
+ if (tempNode.type == "Identifier") {
876
+ superId = tempNode.name;
877
+ }
878
+ }
879
+ // Prevent values that are not allowed to be extends
880
+ if (superClass.type == "ArrayExpression" ||
881
+ superClass.type == "BooleanLiteral" ||
882
+ superClass.type == "BinaryExpression" ||
883
+ superClass.type == "ThisExpression" ||
884
+ superClass.type == "ArrowFunctionExpression" ||
885
+ superClass.type == "BigIntLiteral" ||
886
+ superClass.type == "NumericLiteral" ||
887
+ superClass.type == "NullLiteral" ||
888
+ superClass.type == "AssignmentExpression" ||
889
+ superClass.type == "Super" ||
890
+ superClass.type == "NewExpression" ||
891
+ superClass.type == "DoExpression" ||
892
+ superClass.type == "StringLiteral" ||
893
+ superClass.type == "YieldExpression" ||
894
+ superClass.type == "RecordExpression" ||
895
+ superClass.type == "RegExpLiteral" ||
896
+ superClass.type == "DecimalLiteral" ||
897
+ superClass.type == "BindExpression")
898
+ throw new Error("[compilr error]: class can't extends a not constructor or null");
899
+ if (superId) {
900
+ if (this.indexTemp[superId]) {
901
+ this.writeImportKeys.push(superId);
902
+ }
903
+ }
904
+ }
905
+ }
906
+ else if (item.type == "DoWhileStatement") {
907
+ this.tre(t__namespace.blockStatement([item.body]));
908
+ this.conditionalInTempImport(item.test, currenyContext, remove);
909
+ }
910
+ else if (item.type == "VariableDeclaration") {
911
+ const declaration = item.declarations;
912
+ for (const varDef of declaration) {
913
+ const init = varDef.init;
914
+ const id = varDef.id;
915
+ if (id.type == "Identifier") {
916
+ if (!init && (item.kind == "let" || item.kind == "var"))
917
+ currenyContext[id.name] = {
918
+ status: "wait",
919
+ };
920
+ if (!init)
921
+ throw new Error("[compilr node]: 'const' must has a init");
922
+ currenyContext[id.name] = init;
923
+ }
924
+ }
925
+ }
926
+ else if (item.type == "ReturnStatement") {
927
+ const body = item.argument;
928
+ if (!body)
929
+ continue;
930
+ this.conditionalInTempImport(body, currenyContext, remove);
931
+ }
932
+ else if (item.type == "ExportAllDeclaration" ||
933
+ item.type == "ExportDefaultDeclaration" ||
934
+ item.type == "ExportNamedDeclaration") {
935
+ if (!isTop) {
936
+ throw new Error("[compiler]: export node can't in not top");
937
+ }
938
+ this.CompileData.BuildCache.export.push(item);
939
+ remove();
940
+ }
941
+ else if (item.type == "SwitchStatement") {
942
+ const vaule = item.discriminant;
943
+ this.conditionalInTempImport(vaule, currenyContext, remove);
944
+ for (const caseItem of item.cases) {
945
+ if (caseItem.test) {
946
+ this.conditionalInTempImport(caseItem.test, currenyContext, remove);
947
+ }
948
+ this.tre(t__namespace.blockStatement(caseItem.consequent), currenyContext);
949
+ }
950
+ }
951
+ else if (item.type == "ExpressionStatement") {
952
+ this.conditionalInTempImport(item.expression, currenyContext, remove);
953
+ }
954
+ else if (item.type == "FunctionDeclaration") {
955
+ const funcBody = item.body;
956
+ this.tre(funcBody, currenyContext);
957
+ }
958
+ }
959
+ }
960
+ run() {
961
+ if (!t__namespace.isBlock(this.node))
962
+ throw new Error("[compile error]: can't for a not block");
963
+ this.tre(this.node);
964
+ }
965
+ }
966
+ function compileJSFn(code) {
967
+ const comiler = new CompileJS(Parser.parse(code, { sourceType: "module" }).program);
968
+ comiler.run();
969
+ return comiler.getCompileData();
970
+ }
971
+
972
+ class CompileMain {
973
+ opt;
974
+ main = "";
975
+ constructor(opt) {
976
+ this.opt = opt;
977
+ if (typeof opt.main == "string")
978
+ this.main = path.join(opt.ProjectDir, opt.main);
979
+ }
980
+ async start() {
981
+ if (!this.main)
982
+ throw new Error("[mcx load]: mcx loader must has a main file");
983
+ const mainCode = await fs.readFile(this.main, "utf-8");
984
+ const IR = this.compMain(mainCode);
985
+ if (!IR)
986
+ throw new Error("[mcx compile]: compile main file error");
987
+ IR.setFilePath(this.main);
988
+ for (const importPackage of IR.BuildCache.import) {
989
+ }
990
+ }
991
+ compMain(code) {
992
+ const ext = path.extname(this.main);
993
+ if (ext !== ".js") {
994
+ throw new Error("[load project]: main file must is a javascript.");
995
+ }
996
+ const ir = compileJSFn(code);
997
+ return ir;
998
+ }
999
+ }
1000
+
1001
+ /*import _compile from "./_compile.js"*/
1002
+ /**
1003
+ * @description - this is a function factory to generate mcxProject
1004
+ */
1005
+ function CompileMcxProject(BuildOpt) {
1006
+ return (new Compile(BuildOpt)).start();
1007
+ }
1008
+ class Compile {
1009
+ BuildOpt;
1010
+ constructor(BuildOpt) {
1011
+ this.BuildOpt = BuildOpt;
1012
+ // 类型验证
1013
+ if (!McxUtlis.TypeVerify(this.BuildOpt, {
1014
+ cacheDir: "string",
1015
+ main: "string",
1016
+ moduleDir: "string",
1017
+ output: "string",
1018
+ isCache: "boolean"
1019
+ })) {
1020
+ throw new TypeError("[compile checker]Input Opt is not right");
1021
+ }
1022
+ }
1023
+ async start() {
1024
+ const compileMain = new CompileMain(this.BuildOpt);
1025
+ await compileMain.start();
1026
+ }
1027
+ }
1028
+
1029
+ async function CompileMcxDir(BuildOpt) {
1030
+ await CompileMcxProject(BuildOpt);
1031
+ }
1032
+
1033
+ var index = {
1034
+ load: CompileMcxDir,
1035
+ AST: AST,
1036
+ utils: McxUtlis,
1037
+ };
1038
+
1039
+ module.exports = index;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@mbler/mcx-core",
3
+ "version": "0.0.2-alpha",
4
+ "description": "a DSL compiler of mcx",
5
+ "main": "dist/index.cjs.js",
6
+ "scripts": {
7
+ "test": "node __test__/index.js",
8
+ "build": "rollup -c"
9
+ },
10
+ "keywords": [
11
+ "compiler",
12
+ "minecraft",
13
+ "dsl"
14
+ ],
15
+ "dependencies": {
16
+ "@babel/generator": "7.28.5",
17
+ "@babel/parser": "7.28.5",
18
+ "@babel/types": "7.28.6"
19
+ },
20
+ "module": "module",
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE",
25
+ "__test__"
26
+ ],
27
+ "publishConfig": {
28
+ "registry": "https://registry.npmjs.org",
29
+ "access": "public"
30
+ },
31
+ "author": "ruanhor",
32
+ "license": "MIT",
33
+ "devDependencies": {
34
+ "@rollup/plugin-commonjs": "^29.0.0",
35
+ "@rollup/plugin-json": "^6.1.0",
36
+ "@rollup/plugin-node-resolve": "^16.0.3",
37
+ "@rollup/plugin-typescript": "^12.3.0",
38
+ "@types/babel__generator": "^7.27.0",
39
+ "@types/node": "^25.2.0",
40
+ "rollup": "^4.57.1",
41
+ "tslib": "^2.8.1"
42
+ }
43
+ }