@round-core/shared 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.
- package/package.json +7 -0
- package/src/index.js +205 -0
package/package.json
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Helper: Source Mapper
|
|
2
|
+
export class SourceMapper {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.mappings = []; // [{ gen, orig }]
|
|
5
|
+
this.code = '';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
add(generatedText, originalOffset) {
|
|
9
|
+
const genStart = this.code.length;
|
|
10
|
+
this.code += generatedText;
|
|
11
|
+
this.mappings.push({
|
|
12
|
+
gen: [genStart, this.code.length],
|
|
13
|
+
orig: originalOffset
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
remap(genOffset) {
|
|
18
|
+
for (const m of this.mappings) {
|
|
19
|
+
if (genOffset >= m.gen[0] && genOffset < m.gen[1]) {
|
|
20
|
+
const offsetInBlock = genOffset - m.gen[0];
|
|
21
|
+
return m.orig + offsetInBlock;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Fallback: finding the closest mapping if exactly on boundaries
|
|
25
|
+
if (this.mappings.length > 0) {
|
|
26
|
+
const last = this.mappings[this.mappings.length-1];
|
|
27
|
+
if (genOffset >= last.gen[1]) return last.orig + (last.gen[1] - last.gen[0]);
|
|
28
|
+
}
|
|
29
|
+
return genOffset;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function findBlockEnd(text, startIndex) {
|
|
34
|
+
let depth = 0;
|
|
35
|
+
let i = startIndex;
|
|
36
|
+
if (text[i] !== '{') return -1;
|
|
37
|
+
|
|
38
|
+
let inString = false;
|
|
39
|
+
let quote = '';
|
|
40
|
+
let inLineComment = false;
|
|
41
|
+
let inBlockComment = false;
|
|
42
|
+
|
|
43
|
+
while (i < text.length) {
|
|
44
|
+
const c = text[i];
|
|
45
|
+
const next = text[i+1] || '';
|
|
46
|
+
|
|
47
|
+
if (inLineComment) {
|
|
48
|
+
if (c === '\n') inLineComment = false;
|
|
49
|
+
} else if (inBlockComment) {
|
|
50
|
+
if (c === '*' && next === '/') { inBlockComment = false; i++; }
|
|
51
|
+
} else if (inString) {
|
|
52
|
+
if (c === quote && text[i-1] !== '\\') inString = false;
|
|
53
|
+
else if ((quote === '"' || quote === "'") && c === '\n') inString = false;
|
|
54
|
+
} else {
|
|
55
|
+
if (c === '/' && next === '/') inLineComment = true;
|
|
56
|
+
else if (c === '/' && next === '*') inBlockComment = true;
|
|
57
|
+
else if (c === '"' || c === "'" || c === '`') { inString = true; quote = c; }
|
|
58
|
+
else if (c === '{') depth++;
|
|
59
|
+
else if (c === '}') {
|
|
60
|
+
depth--;
|
|
61
|
+
if (depth === 0) return i + 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
return -1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Preprocess: Updates mapper and returns processed code (via mapper.code)
|
|
70
|
+
export function runPreprocess(text, mapper, globalOffset) {
|
|
71
|
+
let i = 0;
|
|
72
|
+
|
|
73
|
+
let inString = false;
|
|
74
|
+
let quote = '';
|
|
75
|
+
let inLineComment = false;
|
|
76
|
+
let inBlockComment = false;
|
|
77
|
+
|
|
78
|
+
while (i < text.length) {
|
|
79
|
+
const c = text[i];
|
|
80
|
+
const next = text[i+1] || '';
|
|
81
|
+
|
|
82
|
+
if (inLineComment) {
|
|
83
|
+
if (c === '\n') inLineComment = false;
|
|
84
|
+
} else if (inBlockComment) {
|
|
85
|
+
if (c === '*' && next === '/') { inBlockComment = false; mapper.add('*/', globalOffset + i); i+=2; continue; }
|
|
86
|
+
} else if (inString) {
|
|
87
|
+
if (c === quote && text[i-1] !== '\\') inString = false;
|
|
88
|
+
else if ((quote === '"' || quote === "'") && c === '\n') inString = false;
|
|
89
|
+
} else {
|
|
90
|
+
if (c === '/' && next === '/') { inLineComment = true; }
|
|
91
|
+
else if (c === '/' && next === '*') { inBlockComment = true; }
|
|
92
|
+
else if (c === '"' || c === "'" || c === '`') { inString = true; quote = c; }
|
|
93
|
+
else if (c === '{') {
|
|
94
|
+
const match = text.slice(i).match(/^\{\s*(if|else\s+if|else-if|else|for|switch|try|catch|finally)\b/);
|
|
95
|
+
if (match) {
|
|
96
|
+
const end = findBlockEnd(text, i);
|
|
97
|
+
if (end !== -1) {
|
|
98
|
+
const content = text.slice(i + 1, end - 1);
|
|
99
|
+
|
|
100
|
+
let p = 0;
|
|
101
|
+
let validChain = true;
|
|
102
|
+
|
|
103
|
+
const chainSegments = [];
|
|
104
|
+
|
|
105
|
+
// Parse Chain Loop
|
|
106
|
+
while (p < content.length) {
|
|
107
|
+
while (p < content.length && /\s/.test(content[p])) p++;
|
|
108
|
+
if (p >= content.length) break;
|
|
109
|
+
|
|
110
|
+
const sub = content.slice(p);
|
|
111
|
+
const keyMatch = sub.match(/^(if|else\s+if|else-if|else|for|switch|try|catch|finally)\b/);
|
|
112
|
+
|
|
113
|
+
if (!keyMatch) { validChain = false; break; }
|
|
114
|
+
|
|
115
|
+
const keyword = keyMatch[1].replace('-', ' ');
|
|
116
|
+
const partStart = p;
|
|
117
|
+
p += keyMatch[0].length;
|
|
118
|
+
|
|
119
|
+
let head = '';
|
|
120
|
+
let attrs = '';
|
|
121
|
+
let bodyContent = '';
|
|
122
|
+
let bodyStartI = -1;
|
|
123
|
+
let bodyEndI = -1;
|
|
124
|
+
|
|
125
|
+
// Head
|
|
126
|
+
while (p < content.length && /\s/.test(content[p])) p++;
|
|
127
|
+
if (p < content.length && content[p] === '(') {
|
|
128
|
+
const headStart = p;
|
|
129
|
+
let pDepth = 1, h = p + 1;
|
|
130
|
+
let inS=false, qt='';
|
|
131
|
+
while(h < content.length && pDepth > 0) {
|
|
132
|
+
if (inS) { if(content[h]===qt && content[h-1]!=='\\') inS=false; }
|
|
133
|
+
else if ('"\''.includes(content[h])) { inS=true; qt=content[h]; }
|
|
134
|
+
else if (content[h] === '(') pDepth++;
|
|
135
|
+
else if (content[h] === ')') pDepth--;
|
|
136
|
+
h++;
|
|
137
|
+
}
|
|
138
|
+
if (pDepth === 0) { head = content.slice(headStart, h); p = h; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Attrs
|
|
142
|
+
const attrStart = p;
|
|
143
|
+
while (p < content.length) {
|
|
144
|
+
if (content[p] === '{') {
|
|
145
|
+
let back = p - 1;
|
|
146
|
+
while (back >= attrStart && /\s/.test(content[back])) back--;
|
|
147
|
+
if (back >= attrStart && content[back] === '=') {
|
|
148
|
+
const bEnd = findBlockEnd(content, p);
|
|
149
|
+
if (bEnd !== -1) { p = bEnd; continue; }
|
|
150
|
+
} else { break; }
|
|
151
|
+
}
|
|
152
|
+
p++;
|
|
153
|
+
}
|
|
154
|
+
attrs = content.slice(attrStart, p).trim();
|
|
155
|
+
|
|
156
|
+
const guard = /\b(return|throw|function|const|let|var|if|for|while|class|import|export|raise)\b/;
|
|
157
|
+
if (guard.test(attrs) || attrs.includes(';')) { validChain=false; break; }
|
|
158
|
+
|
|
159
|
+
// Body
|
|
160
|
+
if (p < content.length && content[p] === '{') {
|
|
161
|
+
const bEnd = findBlockEnd(content, p);
|
|
162
|
+
if (bEnd !== -1) {
|
|
163
|
+
bodyContent = content.slice(p + 1, bEnd - 1);
|
|
164
|
+
bodyStartI = p + 1;
|
|
165
|
+
bodyEndI = bEnd - 1;
|
|
166
|
+
p = bEnd;
|
|
167
|
+
} else { validChain=false; break; }
|
|
168
|
+
} else { validChain=false; break; }
|
|
169
|
+
|
|
170
|
+
chainSegments.push({ keyword, head, attrs, bodyContent, origStart: partStart,
|
|
171
|
+
bodyRef: { start: bodyStartI, end: bodyEndI } });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (validChain) {
|
|
175
|
+
chainSegments.forEach((seg) => {
|
|
176
|
+
// For head, we use curly braces {} so ESLint can see variable usages
|
|
177
|
+
// We strip outer parens as we'll add them back in the printer to be stable
|
|
178
|
+
const cleanHead = seg.head.trim().replace(/^\(|\)$/g, '');
|
|
179
|
+
const headPart = cleanHead ? `head={${cleanHead}}` : '';
|
|
180
|
+
const safeAttrs = seg.attrs.replace(/"/g, '"');
|
|
181
|
+
const normKind = seg.keyword;
|
|
182
|
+
|
|
183
|
+
const openTag = `<RoundControlFlow kind="${normKind}" ${headPart} _attrs="${safeAttrs}">`;
|
|
184
|
+
const segOrigStart = globalOffset + i + 1 + (seg.origStart || 0);
|
|
185
|
+
|
|
186
|
+
mapper.add(openTag, segOrigStart);
|
|
187
|
+
|
|
188
|
+
const bodyOrigOffset = globalOffset + i + 1 + seg.bodyRef.start;
|
|
189
|
+
runPreprocess(seg.bodyContent, mapper, bodyOrigOffset);
|
|
190
|
+
|
|
191
|
+
mapper.add('</RoundControlFlow>', bodyOrigOffset + seg.bodyContent.length);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
i = end;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
mapper.add(text[i], globalOffset + i);
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
}
|