@bobe-js/lang-core 0.0.1

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.
@@ -0,0 +1,940 @@
1
+ import { NodeType, Tokenizer, Compiler } from 'bobe/compiler';
2
+ import { TypeFlags, SymbolFlags } from 'typescript';
3
+
4
+ const G = {};
5
+ function log(...args) {
6
+ G.log?.info?.(args.join(" "));
7
+ }
8
+ const Virtual_File_Suffix = "__bobe_virtual_file__";
9
+ const Virtual_File_Exp = /__bobe_virtual_file__/;
10
+
11
+ class LRUCache {
12
+ constructor(maxSize) {
13
+ this.cache = /* @__PURE__ */ new Map();
14
+ // 新增 has 方法,检查某个键是否在缓存中
15
+ this.has = this.cache.has.bind(this.cache);
16
+ this.delete = this.cache.delete.bind(this.cache);
17
+ this.maxSize = maxSize;
18
+ }
19
+ get(key) {
20
+ if (!this.cache.has(key)) {
21
+ return void 0;
22
+ }
23
+ const value = this.cache.get(key);
24
+ this.cache.delete(key);
25
+ this.cache.set(key, value);
26
+ return value;
27
+ }
28
+ set(key, value) {
29
+ if (this.cache.has(key)) {
30
+ this.cache.delete(key);
31
+ this.cache.set(key, value);
32
+ } else {
33
+ if (this.cache.size >= this.maxSize) {
34
+ this.cache.delete(this.cache.keys().next().value);
35
+ }
36
+ this.cache.set(key, value);
37
+ }
38
+ }
39
+ }
40
+ const cache = new LRUCache(1e3);
41
+ function memo(fn) {
42
+ const wrap = (v) => {
43
+ const res = fn(v);
44
+ cache.set(v, res);
45
+ return res;
46
+ };
47
+ return wrap;
48
+ }
49
+ function createMemo() {
50
+ const cache2 = new LRUCache(1e3);
51
+ return function memo2(fn) {
52
+ const wrap = (v) => {
53
+ const res = fn(v);
54
+ cache2.set(v, res);
55
+ return res;
56
+ };
57
+ return wrap;
58
+ };
59
+ }
60
+ function uniqBy(arr, keyFn) {
61
+ const seen = /* @__PURE__ */ new Set();
62
+ return arr.filter((item) => {
63
+ const key = keyFn(item);
64
+ if (seen.has(key)) return false;
65
+ seen.add(key);
66
+ return true;
67
+ });
68
+ }
69
+ function getVirtualName(fileName) {
70
+ const dotI = fileName.lastIndexOf(".");
71
+ const rawName = fileName.slice(0, dotI);
72
+ const suffix = fileName.slice(dotI);
73
+ return rawName + Virtual_File_Suffix + suffix;
74
+ }
75
+ function getRealName(virtualFileName) {
76
+ return virtualFileName.replace(Virtual_File_Exp, "");
77
+ }
78
+ function isVirtualFile(fileName) {
79
+ return fileName.match(Virtual_File_Exp);
80
+ }
81
+ function isOverlap(start1, end1, start2, end2) {
82
+ return start1 < end2 && start2 < end1;
83
+ }
84
+ function inInsBrace(content, targetIndex) {
85
+ let stack = [];
86
+ for (let i = 0; i < content.length; i++) {
87
+ if (i === targetIndex) {
88
+ return stack.length > 0 && stack[stack.length - 1] === "block";
89
+ }
90
+ const char = content[i];
91
+ const nextChar = content[i + 1];
92
+ if (char === "$" && nextChar === "{") {
93
+ stack.push("expression");
94
+ i++;
95
+ continue;
96
+ }
97
+ if (char === "{") {
98
+ stack.push("block");
99
+ continue;
100
+ }
101
+ if (char === "}") {
102
+ stack.pop();
103
+ continue;
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+ function isBobeIdentifier(node, tss) {
109
+ return tss.isIdentifier(node.tag) && node.tag.text === "bobe";
110
+ }
111
+ function isBobeTemplate(node, tss) {
112
+ return tss.isTaggedTemplateExpression(node) && isBobeIdentifier(node, tss);
113
+ }
114
+ function sfHasBobeTemplate(sf, tss) {
115
+ let hasBobe = false;
116
+ function visit(node) {
117
+ if (hasBobe) {
118
+ return;
119
+ }
120
+ if (isBobeTemplate(node, tss)) {
121
+ hasBobe = true;
122
+ } else {
123
+ tss.forEachChild(node, visit);
124
+ }
125
+ }
126
+ visit(sf);
127
+ return hasBobe;
128
+ }
129
+ const strHasBobeTemplate = (str) => str.match(/bobe(?:\\s*(<)([^`]*)(>))?`/);
130
+ function findPrecedingClassNode(targetNode, sourceFile, tss) {
131
+ let result, alreadyCrossTargetNode = false;
132
+ function visit(node) {
133
+ if (node.pos >= targetNode.pos || alreadyCrossTargetNode || result) {
134
+ return alreadyCrossTargetNode = true;
135
+ }
136
+ if (isClass(node, tss) && node.name && contains(node, targetNode)) {
137
+ result = node;
138
+ }
139
+ tss.forEachChild(node, visit);
140
+ }
141
+ visit(sourceFile);
142
+ return result;
143
+ }
144
+ function findPrecedingBobeTemplate(targetNode, classNode, tss) {
145
+ let result;
146
+ function visit(node) {
147
+ if (node.pos >= targetNode.pos) return;
148
+ if (isBobeTemplate(node, tss)) {
149
+ result = node;
150
+ }
151
+ tss.forEachChild(node, visit);
152
+ }
153
+ visit(classNode);
154
+ return result;
155
+ }
156
+ function getClassMembersInClass(classNode, tss) {
157
+ const names = [];
158
+ for (const member of classNode.members) {
159
+ if (isClassProp(member, tss)) {
160
+ names.push(member);
161
+ }
162
+ }
163
+ return names;
164
+ }
165
+ function getClassMemberNames(className, rangeNode, tss) {
166
+ const names = [];
167
+ function visit(node) {
168
+ if (isClass(node, tss) && node.name?.text === className) {
169
+ for (const member of node.members) {
170
+ if (isClassProp(member, tss)) {
171
+ names.push(member);
172
+ }
173
+ }
174
+ return;
175
+ }
176
+ tss.forEachChild(node, visit);
177
+ }
178
+ visit(rangeNode);
179
+ return names;
180
+ }
181
+ function calcAbsSourceMap(cursorOffset, templates, isVirtualCursor = false) {
182
+ const compareKey = isVirtualCursor ? "codeOffset" : "originOffset";
183
+ const startKey = isVirtualCursor ? "virtualStart" : "templateStart";
184
+ for (let i = templates.length; i--; ) {
185
+ const template = templates[i];
186
+ if (template[startKey] < cursorOffset) {
187
+ for (const map of template.sourceMap) {
188
+ if (cursorOffset >= map[compareKey] && cursorOffset <= map[compareKey] + map.length) {
189
+ const { codeOffset: virtualStart, originOffset: originStart, length } = map;
190
+ const dt = cursorOffset - map[compareKey];
191
+ return {
192
+ virtualStart,
193
+ originStart,
194
+ virtualOffset: virtualStart + dt,
195
+ originOffset: originStart + dt,
196
+ length
197
+ };
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+ function inWitchVirtualPart(absCursorOffset, templates) {
204
+ for (const template of templates) {
205
+ const range = template.headTemplate?.findRange(absCursorOffset);
206
+ if (range) {
207
+ return { template, part: "headTemplate", range };
208
+ } else {
209
+ for (const area of template?.headAreas || []) {
210
+ const found = area.findRange(absCursorOffset);
211
+ if (found) {
212
+ return { template, part: "headClass", range: found };
213
+ }
214
+ }
215
+ }
216
+ }
217
+ return { template: void 0, part: void 0, range: void 0 };
218
+ }
219
+ function calcHeadSourceMap(absCursorOffset, templates) {
220
+ let virtualStart;
221
+ let originStart, length;
222
+ for (const tmpl of templates) {
223
+ if (!inVirtualHead(tmpl.headMap, { start: absCursorOffset }, tmpl)) continue;
224
+ const cursorOffset = absCursorOffset - tmpl.iifeStartInVirtual;
225
+ for (const entry of tmpl.headMap) {
226
+ if (cursorOffset >= entry.codeOffset && cursorOffset <= entry.codeOffset + entry.length) {
227
+ virtualStart = tmpl.iifeStartInVirtual + entry.codeOffset;
228
+ originStart = entry.originOffset;
229
+ const dt = cursorOffset - entry.codeOffset;
230
+ length = entry.length;
231
+ return {
232
+ virtualStart,
233
+ originStart,
234
+ virtualOffset: virtualStart + dt,
235
+ originOffset: originStart + dt,
236
+ length
237
+ };
238
+ }
239
+ }
240
+ }
241
+ }
242
+ function fixTextSpan(textSpan, code, map) {
243
+ const originalCode = code.slice(map.originStart, map.originStart + map.length);
244
+ if (textSpan.length < map.length && !originalCode.match(domPropertyExp)) {
245
+ const { start, length } = textSpan;
246
+ const targetStart = map.originOffset - (map.virtualOffset - start);
247
+ return { start: targetStart, length };
248
+ }
249
+ return { start: map.originStart, length: map.length };
250
+ }
251
+ function getPosTemplateCtx(info, tss, fileName, position) {
252
+ const sf = info.languageService.getProgram()?.getSourceFile(fileName);
253
+ if (!sf) return null;
254
+ const node = findNodeAtPosition(tss, sf, position);
255
+ if (!node) return null;
256
+ const templateNode = getValidBobeTemplateNode(tss, node);
257
+ if (!templateNode || position <= templateNode.pos) return null;
258
+ const baseOffset = templateNode.getStart() + 1;
259
+ const ctx = makeContext(templateNode, sf, fileName, baseOffset);
260
+ const relPos = getRelativePosition(info, baseOffset, fileName, position);
261
+ return { ctx, relPos };
262
+ }
263
+ function getSourceFileAndNode(info, tss, fileName, position) {
264
+ const sf = info.languageService.getProgram()?.getSourceFile(fileName);
265
+ if (!sf) return null;
266
+ const node = findNodeAtPosition(tss, sf, position);
267
+ if (!node) return null;
268
+ return { sf, node };
269
+ }
270
+ function getValidBobeTemplateNode(tss, node) {
271
+ switch (node.kind) {
272
+ case tss.SyntaxKind.TaggedTemplateExpression: {
273
+ const t = node;
274
+ return isBobeIdentifier(t, tss) ? t.template : void 0;
275
+ }
276
+ case tss.SyntaxKind.NoSubstitutionTemplateLiteral: {
277
+ const p = node.parent;
278
+ return p && isBobeTemplate(p, tss) ? node : void 0;
279
+ }
280
+ case tss.SyntaxKind.TemplateHead:
281
+ return node.parent?.parent ? getValidBobeTemplateNode(tss, node.parent.parent) : void 0;
282
+ case tss.SyntaxKind.TemplateMiddle:
283
+ case tss.SyntaxKind.TemplateTail:
284
+ return node.parent?.parent?.parent ? getValidBobeTemplateNode(tss, node.parent.parent.parent) : void 0;
285
+ default:
286
+ return void 0;
287
+ }
288
+ }
289
+ function getRelativePosition(info, baseOffset, fileName, position) {
290
+ const scriptInfo = info.project.getScriptInfo(fileName);
291
+ if (!scriptInfo) return { line: 0, column: 0, offset: position - baseOffset };
292
+ const baseLoc = scriptInfo.positionToLineOffset(baseOffset);
293
+ const cursorLoc = scriptInfo.positionToLineOffset(position);
294
+ const bl = baseLoc.line - 1, bc = baseLoc.offset - 1;
295
+ const cl = cursorLoc.line - 1, cc = cursorLoc.offset - 1;
296
+ return { line: cl - bl, column: cl === bl ? cc - bc : cc, offset: position - baseOffset };
297
+ }
298
+ function makeContext(templateNode, sf, fileName, baseOffset) {
299
+ return { node: templateNode, fileName, text: templateNode.getText().slice(1, -1), sf, baseOffset };
300
+ }
301
+ function findNodeAtPosition(tss, sf, position) {
302
+ function find(node) {
303
+ if (position >= node.getStart() && position < node.getEnd()) {
304
+ return tss.forEachChild(node, find) || node;
305
+ }
306
+ return void 0;
307
+ }
308
+ return find(sf);
309
+ }
310
+ function findTemplateTypePos(templates, pos) {
311
+ for (const tmpl of templates) {
312
+ const { typeMap } = tmpl;
313
+ if (!typeMap) continue;
314
+ const { originOffset, codeOffset, length } = typeMap;
315
+ if (originOffset <= pos && pos < originOffset + length) {
316
+ const dt = pos - originOffset;
317
+ return {
318
+ virtualStart: codeOffset,
319
+ originStart: originOffset,
320
+ virtualOffset: codeOffset + dt,
321
+ originOffset: originOffset + dt,
322
+ length
323
+ };
324
+ }
325
+ }
326
+ }
327
+ const domPropertyExp = /^[a-zA-Z][\w-]*$/;
328
+ const AND = `__BOBE_AND_${Date.now().toString(36)}__`;
329
+ function isClassProp(node, tss) {
330
+ return tss.isPropertyDeclaration(node) || tss.isMethodDeclaration(node) && tss.isIdentifier(node.name);
331
+ }
332
+ function isClass(node, tss) {
333
+ return tss.isClassDeclaration(node) || tss.isClassExpression(node);
334
+ }
335
+ function* getSharedItems(arr1, arr2, isEqual, isBigger) {
336
+ const it1 = arr1[Symbol.iterator]();
337
+ const it2 = arr2[Symbol.iterator]();
338
+ let res1 = it1.next();
339
+ let res2 = it2.next();
340
+ while (!res1.done && !res2.done) {
341
+ const v1 = res1.value;
342
+ const v2 = res2.value;
343
+ if (isEqual(v1, v2)) {
344
+ yield [v1, v2];
345
+ res1 = it1.next();
346
+ res2 = it2.next();
347
+ } else if (isBigger(v1, v2)) {
348
+ res2 = it2.next();
349
+ } else {
350
+ res1 = it1.next();
351
+ }
352
+ }
353
+ }
354
+ const inVirtualPart = (sf, textSpan) => textSpan.start > sf.getFullWidth();
355
+ const inVirtualHead = (headMap, textSpan, tmpl) => tmpl.iifeStartInVirtual + headMap.range[0] <= textSpan.start && textSpan.start < tmpl.iifeStartInVirtual + headMap.range[1];
356
+ const contains = (parent, child) => (
357
+ // parent.pos 是包含注释和空白的起始位置
358
+ // parent.getStart() 是代码实际开始的位置
359
+ child.getStart() >= parent.getStart() && child.getEnd() <= parent.getEnd()
360
+ );
361
+ const processHandlers = (fns, node) => {
362
+ for (const handler of fns) {
363
+ const shouldSkip = handler(node);
364
+ if (shouldSkip != null) {
365
+ return shouldSkip;
366
+ }
367
+ }
368
+ return 0;
369
+ };
370
+ class Range {
371
+ constructor(start, end) {
372
+ this.start = start;
373
+ this.end = end;
374
+ }
375
+ contains(pos) {
376
+ return this.start <= pos && pos < this.end;
377
+ }
378
+ }
379
+ class Area {
380
+ constructor() {
381
+ this.ranges = [];
382
+ }
383
+ get start() {
384
+ return this.ranges[0]?.start || -1;
385
+ }
386
+ get end() {
387
+ return this.ranges[this.ranges.length - 1]?.end || -1;
388
+ }
389
+ findRange(pos) {
390
+ if (!Range.prototype.contains.call(this, pos)) {
391
+ return;
392
+ }
393
+ for (const range of this.ranges) {
394
+ if (range.contains(pos)) {
395
+ return range;
396
+ }
397
+ }
398
+ }
399
+ addRange(start, end) {
400
+ this.ranges.push(new Range(start, end));
401
+ }
402
+ }
403
+
404
+ class Dent {
405
+ constructor(base) {
406
+ this.base = base;
407
+ this.stack = [base];
408
+ }
409
+ get v() {
410
+ const length = this.stack[this.stack.length - 1];
411
+ return new Array(length).fill(" ").join("");
412
+ }
413
+ indent() {
414
+ const length = this.stack[this.stack.length - 1];
415
+ this.stack.push(length + 2);
416
+ }
417
+ dedent() {
418
+ this.stack.pop();
419
+ }
420
+ }
421
+ const BRACE_REG = /(^\$\{)|(^\{)|(\}$)/g;
422
+ const BOBE_PREFIX = "$Bobe" + Date.now().toString(36);
423
+ const BOBE_DOM_PROP_TRANSFER = `type ${BOBE_PREFIX}ToMap<K extends string, T> = {
424
+ [P in K]?: T;
425
+ };
426
+ type ${BOBE_PREFIX}BooleanProps = ${BOBE_PREFIX}ToMap<
427
+ 'disabled' | 'checked' | 'selected' | 'readonly' | 'required' |
428
+ 'multiple' | 'hidden' | 'autofocus' | 'novalidate' | 'ismap' |
429
+ 'open' | 'reversed' | 'indeterminate',
430
+ boolean|string|undefined|null
431
+ >;
432
+ type ${BOBE_PREFIX}NumericProps = ${BOBE_PREFIX}ToMap<
433
+ 'style'|'value' | 'placeholder' | 'title' | 'alt' | 'width' | 'height' | 'columnCount' | 'tabIndex' | 'maxLength' |
434
+ 'minLength' | 'size' | 'rows' | 'cols' | 'span' | 'start' |
435
+ 'valueAsNumber' | 'max' | 'min' | 'step',
436
+ string|number|undefined|null
437
+ >;
438
+ type ${BOBE_PREFIX}NativeProperties = ${BOBE_PREFIX}BooleanProps & ${BOBE_PREFIX}NumericProps;
439
+ type ${BOBE_PREFIX}CreateTextOrComponent = {
440
+ <T>(a: {defineProps?: T} & Record<any, any>): T;
441
+ <T extends new (...args: any[]) =>any>(input: T): InstanceType<T>;
442
+ (input: any): Text;
443
+ };
444
+ let ${BOBE_PREFIX}_h!:<K extends keyof HTMLElementTagNameMap>(
445
+ tag: K,
446
+ options?: ElementCreationOptions
447
+ ) => Omit<HTMLElementTagNameMap[K], keyof ${BOBE_PREFIX}NativeProperties |'textContent' > & { text: string|number|undefined|null } & ${BOBE_PREFIX}NativeProperties & Record<string, any>;
448
+ let ${BOBE_PREFIX}_t!: ${BOBE_PREFIX}CreateTextOrComponent;
449
+ `;
450
+ class Bobe2ts {
451
+ constructor(c, template, templateStart, virtualStart, templateCode) {
452
+ this.c = c;
453
+ this.template = template;
454
+ this.templateStart = templateStart;
455
+ this.virtualStart = virtualStart;
456
+ this.templateCode = templateCode;
457
+ this.res = {
458
+ output: "",
459
+ input: "",
460
+ sourceMap: []
461
+ };
462
+ this.dent = new Dent(2);
463
+ this.lines = [];
464
+ this.output = ``;
465
+ this.createSetPropsExp = (props, name) => {
466
+ const nameDot = `${name}.`;
467
+ props.forEach((_prop) => {
468
+ const prop = _prop;
469
+ const loc = prop.key.loc;
470
+ let { source: key } = loc;
471
+ this.map(this.off(prop.key), this.output.length + nameDot.length, key.length);
472
+ let replaceCount = 0;
473
+ key = key.replace(/\-(\w)/g, (_, match) => {
474
+ const res = match.toUpperCase();
475
+ replaceCount++;
476
+ return res;
477
+ });
478
+ key = key + new Array(replaceCount).fill(" ").join("");
479
+ const assignLeft = `${nameDot}${key}=`;
480
+ this.output += assignLeft;
481
+ if (!prop.value) {
482
+ this.output += "null;";
483
+ return;
484
+ }
485
+ if (prop.inlineName) {
486
+ this.output += `${prop.inlineName} as any;`;
487
+ return;
488
+ }
489
+ let { source: value } = prop.value.loc;
490
+ if (prop.value.type === NodeType.DynamicValue) {
491
+ value = value.replace(BRACE_REG, " ");
492
+ }
493
+ this.map(this.off(prop.value), this.output.length, value.length);
494
+ this.output += value + ";";
495
+ });
496
+ };
497
+ this.idg = c.idg || new IdGenerator();
498
+ this.program = c.program;
499
+ const tokenizer = this.tokenizer = new Tokenizer(() => void 0, false);
500
+ tokenizer.setCode(templateCode);
501
+ this.compiler = new Compiler(tokenizer, {
502
+ parsePropertyInlineFragment: {
503
+ enter: (node) => {
504
+ const prop = node.parent;
505
+ const component = prop.parent;
506
+ const { varName: cmpInsName, templateSpan } = component.componentName;
507
+ const key = prop.key.key;
508
+ const inlineName = this.idg.name;
509
+ this.idg.i++;
510
+ prop.inlineName = inlineName;
511
+ this.output += `let ${inlineName}=(`;
512
+ const rightBrace = `}: NonNullable<NonNullable<(typeof ${cmpInsName})['${key}']>['defineProps']>) => {
513
+ `;
514
+ if (templateSpan && this.program) {
515
+ const area = new Area();
516
+ this.output += "{";
517
+ const checker = this.program.getTypeChecker();
518
+ const gotType = checker.getTypeAtLocation(templateSpan.expression);
519
+ const exec = new PropertyExtractor(checker, key, templateSpan.expression);
520
+ const names = exec.extractPropertyNames(gotType);
521
+ for (const name of names) {
522
+ const start = this.output.length;
523
+ this.output += `${name}:${name},`;
524
+ area.addRange(this.virtualStart + start, this.virtualStart + this.output.length);
525
+ }
526
+ this.output += rightBrace;
527
+ this.template.headAreas.push(area);
528
+ } else {
529
+ this.c.undoneDocPoint.push(this.output.length);
530
+ this.output += `{${rightBrace}`;
531
+ }
532
+ },
533
+ leave: () => {
534
+ this.output += `};
535
+ `;
536
+ }
537
+ },
538
+ parseElementNode: {
539
+ propsAdded: (node) => {
540
+ const _node = node;
541
+ const tagName = _node.tagName;
542
+ const varName = this.idg.name;
543
+ this.output += `${this.dent.v}let ${varName}=${BOBE_PREFIX}_h('`;
544
+ this.map(this.off(_node), this.output.length, tagName.length);
545
+ this.output += `${tagName}');`;
546
+ this.createSetPropsExp(_node.props, varName);
547
+ this.output += `
548
+ `;
549
+ this.idg.i++;
550
+ }
551
+ },
552
+ parseName: {
553
+ leave: (node) => {
554
+ const varName = this.idg.name;
555
+ this.idg.i++;
556
+ const name = node;
557
+ name.varName = varName;
558
+ const source = name.loc.source;
559
+ const sourceName = source.replace(BRACE_REG, (match) => {
560
+ if (match.length === 1) return " ";
561
+ return " ";
562
+ });
563
+ this.output += `${this.dent.v}let ${varName}=${BOBE_PREFIX}_t(`;
564
+ const map = this.map(this.off(name), this.output.length, source.length);
565
+ this.output += `${sourceName});
566
+ `;
567
+ if (name.type !== NodeType.StaticValue) {
568
+ return;
569
+ }
570
+ let ins;
571
+ while ((ins = this.nextInsExp()) != null) {
572
+ const insStart = ins.getFullStart();
573
+ if (insStart > map.originOffset) {
574
+ if (insStart < map.originOffset + map.length) {
575
+ name.templateSpan = ins;
576
+ }
577
+ break;
578
+ }
579
+ }
580
+ }
581
+ },
582
+ parseComponentNode: {
583
+ propsAdded: (node) => {
584
+ const _node = node;
585
+ const name = _node.componentName;
586
+ this.createSetPropsExp(_node.props, name.varName);
587
+ this.output += `
588
+ `;
589
+ }
590
+ },
591
+ parseConditionalNode: {
592
+ propsAdded: (node) => {
593
+ const _node = node;
594
+ const cond = _node.condition;
595
+ const condVal = cond.loc.source;
596
+ const ifHeadS = `${this.dent.v}if(`;
597
+ this.output += ifHeadS;
598
+ this.map(this.off(cond), this.output.length, condVal.length);
599
+ this.output += condVal + `){
600
+ `;
601
+ this.dent.indent();
602
+ },
603
+ leave: (node) => {
604
+ this.dent.dedent();
605
+ this.output += `${this.dent.v}}
606
+ `;
607
+ }
608
+ },
609
+ parseLoopNode: {
610
+ propsAdded: (node) => {
611
+ const _node = node;
612
+ this.output += `${this.dent.v}`;
613
+ const { collection: arr, item, index, key } = _node;
614
+ this.map(this.off(arr), this.output.length, arr.loc.source.length);
615
+ this.output += arr.loc.source + ".forEach((";
616
+ this.map(this.off(item), this.output.length, item.loc.source.length);
617
+ this.output += item.loc.source;
618
+ if (index) {
619
+ this.output += ",";
620
+ this.map(this.off(index), this.output.length, index.loc.source.length);
621
+ this.output += index.loc.source;
622
+ }
623
+ this.output += `)=>{`;
624
+ if (key) {
625
+ this.output += `let ${this.idg.k}=`;
626
+ this.map(this.off(key), this.output.length, key.loc.source.length);
627
+ this.output += key.loc.source + ";";
628
+ }
629
+ this.output += "\n";
630
+ this.dent.indent();
631
+ },
632
+ leave: (node) => {
633
+ this.dent.dedent();
634
+ this.output += `${this.dent.v}});
635
+ `;
636
+ }
637
+ }
638
+ });
639
+ }
640
+ map(templateOffset, codeOffset, length) {
641
+ const item = {
642
+ originOffset: this.templateStart + templateOffset,
643
+ codeOffset: this.virtualStart + codeOffset,
644
+ length
645
+ };
646
+ this.res.sourceMap.push(item);
647
+ return item;
648
+ }
649
+ nextInsExp() {
650
+ const nextData = this.c.tempStaticIns?.next();
651
+ return nextData?.value;
652
+ }
653
+ off(n) {
654
+ return n.loc.start.offset - 1;
655
+ }
656
+ process() {
657
+ this.compiler.parseProgram();
658
+ return {
659
+ output: this.output,
660
+ input: this.templateCode,
661
+ sourceMap: this.res.sourceMap,
662
+ errors: this.compiler.errors
663
+ };
664
+ }
665
+ }
666
+ class IdGenerator {
667
+ constructor() {
668
+ this.id = Date.now().toString(36);
669
+ this.i = 0;
670
+ }
671
+ get name() {
672
+ return `${BOBE_PREFIX}_var_${this.id}_${this.i}`;
673
+ }
674
+ get h() {
675
+ return `h_${this.id}`;
676
+ }
677
+ get t() {
678
+ return `t_${this.id}`;
679
+ }
680
+ get k() {
681
+ return `${BOBE_PREFIX}_for_key_${this.id}`;
682
+ }
683
+ }
684
+ class PropertyExtractor {
685
+ constructor(checker, key, locationNode) {
686
+ this.checker = checker;
687
+ this.key = key;
688
+ this.locationNode = locationNode;
689
+ }
690
+ /**
691
+ * 主入口:两步提取属性名
692
+ * 1. 解析 midType(通过 key 从原始类型中获取)
693
+ * 2. 从 midType 中提取属性名列表
694
+ * 中间任何一步不符合正向逻辑时返回空数组
695
+ */
696
+ extractPropertyNames(type) {
697
+ if (!type || type.intrinsicName === "error") {
698
+ return [];
699
+ }
700
+ const midType = this.resolveMidType(type);
701
+ if (!midType) return [];
702
+ const midTStr = this.checker.typeToString(midType);
703
+ log("midType:", midTStr);
704
+ return this.extractNamesFromMidType(midType);
705
+ }
706
+ /**
707
+ * Step 1: 获取传入类型对应实例属性的 key 的类型 (midType)
708
+ * 1.1 classType 是类 → 获取实例类型 → 获取 instance.key 的类型
709
+ * 1.2 classType 是 { defineProps?: T } → 获取 defineProps.key 的类型
710
+ */
711
+ resolveMidType(type) {
712
+ if (this.isClassType(type)) {
713
+ const symbol = type.getSymbol() || type.aliasSymbol;
714
+ if (!symbol) return null;
715
+ const instanceType = this.checker.getDeclaredTypeOfSymbol(symbol);
716
+ if (!(instanceType.flags & TypeFlags.Object)) return null;
717
+ const targetProp = instanceType.getProperty(this.key);
718
+ if (!targetProp) return null;
719
+ return this.checker.getTypeOfSymbolAtLocation(targetProp, this.locationNode);
720
+ }
721
+ if (this.isObjectType(type)) {
722
+ const defineProps = type.getProperty("defineProps");
723
+ if (defineProps) {
724
+ const definePropsType = this.checker.getTypeOfSymbolAtLocation(defineProps, this.locationNode);
725
+ const targetProp = definePropsType.getProperty(this.key);
726
+ if (!targetProp) return null;
727
+ return this.checker.getTypeOfSymbolAtLocation(targetProp, this.locationNode);
728
+ }
729
+ }
730
+ return null;
731
+ }
732
+ /**
733
+ * Step 2: 处理 midType,提取属性名列表
734
+ * 2.1 midType 是 class → 获取其实例的所有属性名
735
+ * 2.2 midType 是 { defineProps?: T } → 获取 defineProps 的所有属性名
736
+ */
737
+ extractNamesFromMidType(midType) {
738
+ if (midType.flags & TypeFlags.UnionOrIntersection) {
739
+ const types = midType.types;
740
+ if (types) {
741
+ for (const t of types) {
742
+ const names = this.extractNamesFromMidType(t);
743
+ if (names.length > 0) return names;
744
+ }
745
+ }
746
+ return [];
747
+ }
748
+ if (this.isClassType(midType)) {
749
+ const symbol = midType.getSymbol() || midType.aliasSymbol;
750
+ if (!symbol) return [];
751
+ const instanceType = this.checker.getDeclaredTypeOfSymbol(symbol);
752
+ if (!(instanceType.flags & TypeFlags.Object)) return [];
753
+ return this.getStandardProperties(instanceType);
754
+ }
755
+ if (this.isObjectType(midType)) {
756
+ const defineProps = midType.getProperty("defineProps");
757
+ if (defineProps) {
758
+ const definePropsType = this.checker.getTypeOfSymbolAtLocation(defineProps, this.locationNode);
759
+ return this.extractNamesFromMidType(definePropsType);
760
+ }
761
+ return this.getStandardProperties(midType);
762
+ }
763
+ return [];
764
+ }
765
+ /**
766
+ * 判断是否为 Class(构造函数静态类型)
767
+ */
768
+ isClassType(type) {
769
+ const symbol = type.getSymbol() || type.aliasSymbol;
770
+ if (!symbol) return false;
771
+ return !!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Function));
772
+ }
773
+ /**
774
+ * 判断是否为普通对象类型 (Object)
775
+ */
776
+ isObjectType(type) {
777
+ return !!(type.flags & TypeFlags.Object);
778
+ }
779
+ /**
780
+ * 获取类型的所有属性名(排除 prototype 等元属性)
781
+ */
782
+ getStandardProperties(type) {
783
+ try {
784
+ const props = this.checker.getPropertiesOfType(type);
785
+ return props.map((p) => p.name).filter((name) => name !== "prototype");
786
+ } catch {
787
+ return [];
788
+ }
789
+ }
790
+ }
791
+
792
+ function findBobeTemplatesInClass(classNode, tss, sourceFile, program) {
793
+ const preInfos = [];
794
+ const checker = program?.getTypeChecker();
795
+ function visit(node) {
796
+ if (isBobeTemplate(node, tss)) {
797
+ const template = node.template;
798
+ const typeArg = node.typeArguments?.[0];
799
+ let props = void 0, parent = node.parent, uiName = void 0, typeExp = void 0;
800
+ for (let i = 0; i < 5 && parent; i++) {
801
+ const parentIsAssign = tss.isPropertyDeclaration(parent);
802
+ if (parentIsAssign) {
803
+ uiName = parent.name.getText();
804
+ break;
805
+ }
806
+ }
807
+ if (typeArg && checker && uiName) {
808
+ typeExp = typeArg.getText();
809
+ const typeNode = checker.getTypeAtLocation(typeArg);
810
+ const propNodes = checker.getPropertiesOfType(typeNode);
811
+ props = propNodes.map((prop) => prop.name);
812
+ }
813
+ const raw = tss.isNoSubstitutionTemplateLiteral(template) ? template.rawText ?? template.text : template.getText().slice(1, -1);
814
+ const templateStart = template.getStart() + 1;
815
+ preInfos.push({
816
+ raw,
817
+ uiName,
818
+ typeExp,
819
+ props,
820
+ className: classNode.name?.text || "",
821
+ sf: sourceFile,
822
+ templateStart
823
+ });
824
+ } else {
825
+ tss.forEachChild(node, visit);
826
+ }
827
+ }
828
+ visit(classNode);
829
+ return preInfos;
830
+ }
831
+ let c = {};
832
+ function buildVirtualDocument(sourceFile, tss, program) {
833
+ const source = sourceFile.text;
834
+ const virtualCode = source + "\nexport {};\n" + BOBE_DOM_PROP_TRANSFER;
835
+ const baseVOffset = virtualCode.length;
836
+ c = { tss, program, baseVOffset, virtualCode, templates: [], undoneDocPoint: [] };
837
+ function walk(node) {
838
+ const skip = processHandlers([beginClass, beginTemplate], node);
839
+ if (!skip) tss.forEachChild(node, walk);
840
+ processHandlers([endClass, endTemplate], node);
841
+ }
842
+ walk(sourceFile);
843
+ log("\u865A\u62DF\u6587\u4EF6\n", c.virtualCode);
844
+ const result = { code: c.virtualCode, templates: c.templates, sf: sourceFile };
845
+ c = {};
846
+ return result;
847
+ }
848
+ function beginClass(node) {
849
+ if (!isClass(node, c.tss)) return;
850
+ c.currentClass = node;
851
+ return 0;
852
+ }
853
+ function endClass(node) {
854
+ if (!isClass(node, c.tss)) return;
855
+ c.currentClass = void 0;
856
+ if (c.builtHeadAreas) {
857
+ c.builtHeadAreas = void 0;
858
+ c.idg = void 0;
859
+ c.virtualCode += `}
860
+ `;
861
+ }
862
+ return;
863
+ }
864
+ function beginTemplate(node) {
865
+ if (!c.tss.isTaggedTemplateExpression(node)) return;
866
+ if (!isBobeIdentifier(node, c.tss)) return 1;
867
+ const template = { headAreas: [] };
868
+ if (c.currentClass && !c.builtHeadAreas) {
869
+ c.idg = new IdGenerator();
870
+ const { currentClass } = c;
871
+ const className = currentClass.name.getText();
872
+ const area = new Area();
873
+ c.virtualCode += `{
874
+ const {`;
875
+ const members = getClassMembersInClass(currentClass, c.tss);
876
+ for (const member of members) {
877
+ const nameText = member.name.getText();
878
+ const itemStart = c.virtualCode.length;
879
+ c.virtualCode += `${nameText}:${nameText},`;
880
+ const itemEnd = c.virtualCode.length;
881
+ area.addRange(itemStart, itemEnd);
882
+ }
883
+ c.virtualCode += `} = {} as any as ${className};
884
+ `;
885
+ c.builtHeadAreas = template.headAreas = [area];
886
+ }
887
+ c.virtualCode += "{\n";
888
+ if (node.typeArguments) {
889
+ const typeArg = node.typeArguments[0];
890
+ const checker = c.program?.getTypeChecker();
891
+ if (checker) {
892
+ const typeNode = checker.getTypeAtLocation(typeArg);
893
+ const typeExp = typeArg.getText();
894
+ const originOffset = typeArg.getFullStart();
895
+ const propNodes = checker.getPropertiesOfType(typeNode);
896
+ const area = new Area();
897
+ c.virtualCode += `const {`;
898
+ for (const { name } of propNodes) {
899
+ const itemStart = c.virtualCode.length;
900
+ c.virtualCode += `${name}:${name},`;
901
+ const itemEnd = c.virtualCode.length;
902
+ area.addRange(itemStart, itemEnd);
903
+ }
904
+ c.virtualCode += `} = {} as any as `;
905
+ const codeOffset = c.virtualCode.length;
906
+ c.virtualCode += typeExp;
907
+ template.headTemplate = area;
908
+ template.typeMap = {
909
+ originOffset,
910
+ codeOffset,
911
+ length: typeExp.length
912
+ };
913
+ c.virtualCode += `;
914
+ `;
915
+ }
916
+ }
917
+ if (!c.tss.isNoSubstitutionTemplateLiteral(node.template)) {
918
+ const insArr = Array.from(node.template.templateSpans);
919
+ c.tempStaticIns = insArr[Symbol.iterator]();
920
+ }
921
+ const templateStart = node.template.getFullStart() + 1;
922
+ const virtualStart = c.virtualCode.length;
923
+ const raw = node.template.getText().slice(1, -1);
924
+ const { output, sourceMap, errors } = new Bobe2ts(c, template, templateStart, virtualStart, raw).process();
925
+ c.virtualCode += output + `}
926
+ `;
927
+ template.sourceMap = sourceMap;
928
+ template.errors = errors;
929
+ template.templateStart = templateStart;
930
+ template.virtualStart = virtualStart;
931
+ c.templates.push(template);
932
+ return 1;
933
+ }
934
+ function endTemplate(node) {
935
+ if (!isBobeTemplate(node, c.tss)) return;
936
+ return;
937
+ }
938
+
939
+ export { AND, Area, BOBE_DOM_PROP_TRANSFER, BOBE_PREFIX, Bobe2ts, G, IdGenerator, LRUCache, PropertyExtractor, Range, Virtual_File_Exp, Virtual_File_Suffix, buildVirtualDocument, calcAbsSourceMap, calcHeadSourceMap, contains, createMemo, domPropertyExp, findBobeTemplatesInClass, findNodeAtPosition, findPrecedingBobeTemplate, findPrecedingClassNode, findTemplateTypePos, fixTextSpan, getClassMemberNames, getClassMembersInClass, getPosTemplateCtx, getRealName, getRelativePosition, getSharedItems, getSourceFileAndNode, getValidBobeTemplateNode, getVirtualName, inInsBrace, inVirtualHead, inVirtualPart, inWitchVirtualPart, isBobeIdentifier, isBobeTemplate, isClass, isClassProp, isOverlap, isVirtualFile, log, makeContext, memo, processHandlers, sfHasBobeTemplate, strHasBobeTemplate, uniqBy };
940
+ //# sourceMappingURL=bobe-language-core.esm.js.map