@vesk/compiler 0.1.1 → 0.2.8
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/dist/api-routes.d.ts +6 -1
- package/dist/api-routes.d.ts.map +1 -1
- package/dist/api-routes.js +52 -7
- package/dist/client-codegen.d.ts +15 -0
- package/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +73 -10
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/ir-generator.d.ts.map +1 -1
- package/dist/ir-generator.js +109 -1
- package/dist/md-inline.d.ts +17 -0
- package/dist/md-inline.d.ts.map +1 -0
- package/dist/md-inline.js +149 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +42 -7
- package/dist/router.d.ts +2 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +48 -3
- package/dist/server-codegen.d.ts +1 -1
- package/dist/server-codegen.d.ts.map +1 -1
- package/dist/server-codegen.js +1 -1
- package/dist/server-jsgen.d.ts.map +1 -1
- package/dist/server-jsgen.js +4 -3
- package/dist/server-render.d.ts.map +1 -1
- package/dist/server-render.js +16 -8
- package/dist/server-utils.d.ts +31 -0
- package/dist/server-utils.d.ts.map +1 -1
- package/dist/server-utils.js +478 -52
- package/dist/ssr-store.d.ts +1 -1
- package/dist/ssr-store.d.ts.map +1 -1
- package/dist/ssr-store.js +3 -0
- package/dist/typecheck.d.ts +2 -0
- package/dist/typecheck.d.ts.map +1 -1
- package/dist/typecheck.js +452 -29
- package/dist/types.d.ts +4 -129
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/vesk-plugin.d.ts +2 -0
- package/dist/vesk-plugin.d.ts.map +1 -1
- package/dist/vesk-plugin.js +152 -0
- package/dist/vsk-tsx.d.ts +62 -0
- package/dist/vsk-tsx.d.ts.map +1 -1
- package/dist/vsk-tsx.js +844 -362
- package/package.json +2 -1
- package/dist/package.json +0 -15
package/dist/vsk-tsx.js
CHANGED
|
@@ -1,141 +1,49 @@
|
|
|
1
1
|
import { parse } from '@vesk/compiler/src/parser';
|
|
2
2
|
import { getPropsType } from '@vesk/compiler/src/ir-generator';
|
|
3
|
-
import { skipWhitespace, findBalancedEnd,
|
|
3
|
+
import { skipWhitespace, findBalancedEnd, startsWithIdentifier, stripDeclKeyword, stripTrailingSemicolons, isWhitespaceChar, isIdentStart, isIdentChar, htmlTagEnd, } from '@vesk/compiler/src/scan';
|
|
4
4
|
import { containsIdentifier, isIdentifierImported } from '@vesk/compiler/src/tokens';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
continue;
|
|
34
|
-
if (!decl.init)
|
|
35
|
-
continue;
|
|
36
|
-
const names = (id.elements || [])
|
|
37
|
-
.map((el) => (el ? el.name : null))
|
|
38
|
-
.filter((n) => typeof n === 'string');
|
|
39
|
-
const initText = wrapped.slice(decl.init.start, decl.init.end);
|
|
40
|
-
// The first declarator's replacement must also absorb the statement's
|
|
41
|
-
// `const`/`let`/`var` keyword, which starts at `stmt.start`, not at
|
|
42
|
-
// the declarator's start.
|
|
43
|
-
const start = di === 0 ? stmt.start : decl.start;
|
|
44
|
-
const end = decl.end;
|
|
45
|
-
// Don't duplicate the statement terminator already present after the
|
|
46
|
-
// declaration (avoids `let x = ...;;`).
|
|
47
|
-
const terminator = wrapped[end] === ';' ? '' : ';';
|
|
48
|
-
if (names.length === 0) {
|
|
49
|
-
edits.push({ start: start - prefixLen, end: end - prefixLen, text: `let _ = ${initText}${terminator}` });
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
const annotation = id.typeAnnotation?.typeAnnotation
|
|
53
|
-
? wrapped.slice(id.typeAnnotation.typeAnnotation.start, id.typeAnnotation.typeAnnotation.end).trim()
|
|
54
|
-
: decl.init.typeArguments
|
|
55
|
-
? wrapped.slice(decl.init.typeArguments.start + 1, decl.init.typeArguments.end - 1).trim()
|
|
56
|
-
: '';
|
|
57
|
-
const first = names[0];
|
|
58
|
-
let replacement;
|
|
59
|
-
if (annotation) {
|
|
60
|
-
replacement = `let ${first}: ${annotation} = (${initText} as unknown as ${annotation});`;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
replacement = `let ${first}: any = ${initText};`;
|
|
64
|
-
}
|
|
65
|
-
for (let n = 1; n < names.length; n++) {
|
|
66
|
-
replacement += ` let ${names[n]}: any = ${first};`;
|
|
67
|
-
}
|
|
68
|
-
// The last emitted statement sits directly before `end`; drop its
|
|
69
|
-
// terminator when the source already provides one (avoids `;;`).
|
|
70
|
-
if (wrapped[end] === ';')
|
|
71
|
-
replacement = replacement.slice(0, -1);
|
|
72
|
-
edits.push({ start: start - prefixLen, end: end - prefixLen, text: replacement });
|
|
73
|
-
}
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const kids = [];
|
|
77
|
-
if (stmt.consequent)
|
|
78
|
-
kids.push(stmt.consequent);
|
|
79
|
-
if (stmt.alternate)
|
|
80
|
-
kids.push(stmt.alternate);
|
|
81
|
-
if (Array.isArray(stmt.body))
|
|
82
|
-
kids.push(...stmt.body);
|
|
83
|
-
else if (stmt.body && stmt.body.type)
|
|
84
|
-
kids.push(stmt.body);
|
|
85
|
-
if (Array.isArray(stmt.cases)) {
|
|
86
|
-
for (const c of stmt.cases)
|
|
87
|
-
if (Array.isArray(c.consequent))
|
|
88
|
-
kids.push(...c.consequent);
|
|
89
|
-
}
|
|
90
|
-
if (stmt.handler)
|
|
91
|
-
kids.push(stmt.handler.body);
|
|
92
|
-
if (stmt.finalizer)
|
|
93
|
-
kids.push(stmt.finalizer.body);
|
|
94
|
-
walk(kids);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
walk(ast.body);
|
|
98
|
-
return edits;
|
|
99
|
-
}
|
|
100
|
-
const REWRITER_WRAPPER = 'component __VskRewriter() { ';
|
|
101
|
-
const REWRITER_WRAPPER_LEN = REWRITER_WRAPPER.length;
|
|
102
|
-
function rewriteTrackDecls(text) {
|
|
103
|
-
if (!text.includes('&['))
|
|
104
|
-
return text;
|
|
105
|
-
const wrapped = REWRITER_WRAPPER + text + ' }';
|
|
106
|
-
let ast;
|
|
107
|
-
try {
|
|
108
|
-
ast = parse(wrapped);
|
|
5
|
+
const FULL_DATA = {
|
|
6
|
+
verification: true,
|
|
7
|
+
completion: true,
|
|
8
|
+
semantic: true,
|
|
9
|
+
navigation: true,
|
|
10
|
+
structure: true,
|
|
11
|
+
format: true,
|
|
12
|
+
};
|
|
13
|
+
const REACTIVE_DATA = { ...FULL_DATA, customData: { vesk: { reactive: true } } };
|
|
14
|
+
class VskGen {
|
|
15
|
+
code = '';
|
|
16
|
+
mappings = [];
|
|
17
|
+
styleRegions = [];
|
|
18
|
+
/** Counts track-decl rewrites so typedCells cell names stay unique within a codegen run. */
|
|
19
|
+
cellCount = 0;
|
|
20
|
+
/** Appends `text`; when a source range is given, records a mapping. */
|
|
21
|
+
add(text, srcStart, srcEnd, data = FULL_DATA) {
|
|
22
|
+
const start = this.code.length;
|
|
23
|
+
this.code += text;
|
|
24
|
+
if (text.length > 0 && srcStart !== undefined && srcEnd !== undefined && srcEnd > srcStart) {
|
|
25
|
+
this.mappings.push({
|
|
26
|
+
sourceOffsets: [srcStart],
|
|
27
|
+
generatedOffsets: [start],
|
|
28
|
+
lengths: [srcEnd - srcStart],
|
|
29
|
+
generatedLengths: [text.length],
|
|
30
|
+
data,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
109
33
|
}
|
|
110
|
-
|
|
111
|
-
|
|
34
|
+
/** Appends synthetic text with no source mapping. */
|
|
35
|
+
addRaw(text) {
|
|
36
|
+
this.code += text;
|
|
112
37
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
function stripStyleBlocks(text) {
|
|
119
|
-
const out = [];
|
|
120
|
-
let i = 0;
|
|
121
|
-
while (i < text.length) {
|
|
122
|
-
if (text[i] === '<' && text.slice(i, i + 6) === '<style') {
|
|
123
|
-
const next = i + 6;
|
|
124
|
-
if (next >= text.length || isWhitespaceChar(text[next]) || text[next] === '>' || text[next] === '/') {
|
|
125
|
-
const openEnd = htmlTagEnd(text, i);
|
|
126
|
-
if (openEnd !== -1) {
|
|
127
|
-
const closeIdx = text.indexOf('</style>', openEnd);
|
|
128
|
-
if (closeIdx !== -1) {
|
|
129
|
-
i = closeIdx + '</style>'.length;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
38
|
+
prepend(text) {
|
|
39
|
+
this.code = text + this.code;
|
|
40
|
+
for (const m of this.mappings) {
|
|
41
|
+
m.generatedOffsets = m.generatedOffsets.map((o) => o + text.length);
|
|
134
42
|
}
|
|
135
|
-
out.push(text[i]);
|
|
136
|
-
i++;
|
|
137
43
|
}
|
|
138
|
-
|
|
44
|
+
recordStyle(start, end, content) {
|
|
45
|
+
this.styleRegions.push({ start, end, content });
|
|
46
|
+
}
|
|
139
47
|
}
|
|
140
48
|
function getComponents(ast) {
|
|
141
49
|
const out = [];
|
|
@@ -183,15 +91,394 @@ function getJSXTagName(el) {
|
|
|
183
91
|
return name.namespace.name + ':' + name.name.name;
|
|
184
92
|
return null;
|
|
185
93
|
}
|
|
186
|
-
|
|
94
|
+
/** Collapses newline+whitespace runs to single spaces and records the raw index of every kept character. */
|
|
95
|
+
function collapseWithMap(value) {
|
|
96
|
+
let out = '';
|
|
97
|
+
const map = [];
|
|
98
|
+
let i = 0;
|
|
99
|
+
while (i < value.length) {
|
|
100
|
+
const c = value[i];
|
|
101
|
+
if (c === '\n') {
|
|
102
|
+
out += ' ';
|
|
103
|
+
map.push(i);
|
|
104
|
+
while (i < value.length && isWhitespaceChar(value[i]))
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
out += c;
|
|
109
|
+
map.push(i);
|
|
110
|
+
i++;
|
|
111
|
+
}
|
|
112
|
+
return { text: out, map };
|
|
113
|
+
}
|
|
114
|
+
function isStatementMode(node) {
|
|
115
|
+
const stmts = node.body?.body ?? [];
|
|
116
|
+
return !(stmts.length === 1 && stmts[0].type === 'ReturnStatement');
|
|
117
|
+
}
|
|
118
|
+
/** Index of the first whole-word occurrence of `word` in `text`, or -1. */
|
|
119
|
+
function findFirstKeyword(text, word) {
|
|
120
|
+
let i = 0;
|
|
121
|
+
while (i < text.length) {
|
|
122
|
+
if (isIdentStart(text[i])) {
|
|
123
|
+
let j = i + 1;
|
|
124
|
+
while (j < text.length && isIdentChar(text[j]))
|
|
125
|
+
j++;
|
|
126
|
+
if (text.slice(i, j) === word)
|
|
127
|
+
return i;
|
|
128
|
+
i = j;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
i++;
|
|
132
|
+
}
|
|
133
|
+
return -1;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Removes a trailing `client` keyword (plus surrounding whitespace) from a
|
|
137
|
+
* component header, replacing it with a single trailing space. Returns the
|
|
138
|
+
* kept text and the length of the source range it covers.
|
|
139
|
+
*/
|
|
140
|
+
function stripTrailingClientRange(text) {
|
|
141
|
+
let end = text.length;
|
|
142
|
+
while (end > 0 && isWhitespaceChar(text[end - 1]))
|
|
143
|
+
end--;
|
|
144
|
+
let wordStart = end;
|
|
145
|
+
while (wordStart > 0 && isIdentChar(text[wordStart - 1]))
|
|
146
|
+
wordStart--;
|
|
147
|
+
if (text.slice(wordStart, end) === 'client') {
|
|
148
|
+
return { text: text.slice(0, wordStart).trimEnd() + ' ', end: wordStart };
|
|
149
|
+
}
|
|
150
|
+
return { text, end: text.length };
|
|
151
|
+
}
|
|
152
|
+
/** Index of the first depth-0 whole-word occurrence of `sep` in `text`, or -1 (splitTopLevel semantics). */
|
|
153
|
+
function findTopLevelSep(text, sep) {
|
|
154
|
+
let depth = 0;
|
|
155
|
+
let i = 0;
|
|
156
|
+
while (i < text.length) {
|
|
157
|
+
const c = text[i];
|
|
158
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
159
|
+
let j = i + 1;
|
|
160
|
+
while (j < text.length) {
|
|
161
|
+
const q = text[j];
|
|
162
|
+
if (q === '\\') {
|
|
163
|
+
j += 2;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (q === c) {
|
|
167
|
+
j++;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
j++;
|
|
171
|
+
}
|
|
172
|
+
i = j;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (c === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
|
|
176
|
+
let j = i + 2;
|
|
177
|
+
if (text[i + 1] === '/') {
|
|
178
|
+
while (j < text.length && text[j] !== '\n')
|
|
179
|
+
j++;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
while (j < text.length && !(text[j] === '*' && text[j + 1] === '/'))
|
|
183
|
+
j++;
|
|
184
|
+
j = Math.min(j + 2, text.length);
|
|
185
|
+
}
|
|
186
|
+
i = j;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (c === '(' || c === '[' || c === '{')
|
|
190
|
+
depth++;
|
|
191
|
+
else if (c === ')' || c === ']' || c === '}') {
|
|
192
|
+
if (depth > 0)
|
|
193
|
+
depth--;
|
|
194
|
+
}
|
|
195
|
+
else if (depth === 0 && c === sep[0] && text.slice(i, i + sep.length) === sep) {
|
|
196
|
+
const before = i === 0 ? ' ' : text[i - 1];
|
|
197
|
+
const after = i + sep.length < text.length ? text[i + sep.length] : ' ';
|
|
198
|
+
if (!isIdentChar(before) && !isIdentChar(after))
|
|
199
|
+
return i;
|
|
200
|
+
}
|
|
201
|
+
i++;
|
|
202
|
+
}
|
|
203
|
+
return -1;
|
|
204
|
+
}
|
|
205
|
+
function trailingWsLen(text) {
|
|
206
|
+
let n = 0;
|
|
207
|
+
while (n < text.length && isWhitespaceChar(text[text.length - 1 - n]))
|
|
208
|
+
n++;
|
|
209
|
+
return n;
|
|
210
|
+
}
|
|
211
|
+
function leadingWsLen(text) {
|
|
212
|
+
let n = 0;
|
|
213
|
+
while (n < text.length && isWhitespaceChar(text[n]))
|
|
214
|
+
n++;
|
|
215
|
+
return n;
|
|
216
|
+
}
|
|
217
|
+
function isTrackDeclStatement(stmt) {
|
|
218
|
+
if (!stmt || stmt.type !== 'VariableDeclaration')
|
|
219
|
+
return false;
|
|
220
|
+
return (stmt.declarations || []).some((d) => d && d.id && d.id.type === 'ArrayPattern' && d.id.lazy === true && d.init);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Emits the rewrite for a `const &[count] = track(0)` declarator. Mirrors
|
|
224
|
+
* the exact text produced by the previous string-level rewrite: the first
|
|
225
|
+
* declarator absorbs the statement's `const`/`let`/`var` keyword, the
|
|
226
|
+
* replacement always ends with `;` unless the source statement already
|
|
227
|
+
* provides a terminator (checked on the emitted statement text, exactly
|
|
228
|
+
* like the old `wrapped[end] === ';'` rule).
|
|
229
|
+
*/
|
|
230
|
+
function emitTrackDeclStatement(g, source, stmt, indent, isLast, opts) {
|
|
231
|
+
const fullText = stripTrailingSemicolons(source.slice(stmt.start, stmt.end).trimEnd());
|
|
232
|
+
const text = fullText + (!fullText.endsWith('}') && !isLast ? ';' : '');
|
|
233
|
+
const decls = stmt.declarations || [];
|
|
234
|
+
g.add(indent);
|
|
235
|
+
let prevEnd = stmt.start;
|
|
236
|
+
for (let di = 0; di < decls.length; di++) {
|
|
237
|
+
const decl = decls[di];
|
|
238
|
+
const id = decl.id;
|
|
239
|
+
const isLazy = id && id.type === 'ArrayPattern' && id.lazy === true && decl.init;
|
|
240
|
+
if (!isLazy) {
|
|
241
|
+
g.addRaw(source.slice(prevEnd, decl.start));
|
|
242
|
+
g.add(source.slice(decl.start, decl.end), decl.start, decl.end);
|
|
243
|
+
prevEnd = decl.end;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (di > 0)
|
|
247
|
+
g.addRaw(source.slice(prevEnd, decl.start));
|
|
248
|
+
const ch = text[decl.end - stmt.start] ?? ' ';
|
|
249
|
+
const elements = (id.elements || []);
|
|
250
|
+
const names = elements
|
|
251
|
+
.map((el) => (el ? el.name : null))
|
|
252
|
+
.filter((n) => typeof n === 'string');
|
|
253
|
+
const initText = source.slice(decl.init.start, decl.init.end);
|
|
254
|
+
if (names.length === 0) {
|
|
255
|
+
g.add('let _ = ');
|
|
256
|
+
g.add(initText, decl.init.start, decl.init.end);
|
|
257
|
+
if (ch !== ';')
|
|
258
|
+
g.addRaw(';');
|
|
259
|
+
prevEnd = decl.end;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const annotation = id.typeAnnotation?.typeAnnotation
|
|
263
|
+
? source.slice(id.typeAnnotation.typeAnnotation.start, id.typeAnnotation.typeAnnotation.end).trim()
|
|
264
|
+
: decl.init.typeArguments
|
|
265
|
+
? source.slice(decl.init.typeArguments.start + 1, decl.init.typeArguments.end - 1).trim()
|
|
266
|
+
: '';
|
|
267
|
+
const annStart = id.typeAnnotation?.typeAnnotation
|
|
268
|
+
? id.typeAnnotation.typeAnnotation.start
|
|
269
|
+
: decl.init.typeArguments
|
|
270
|
+
? decl.init.typeArguments.start + 1
|
|
271
|
+
: -1;
|
|
272
|
+
const annEnd = id.typeAnnotation?.typeAnnotation
|
|
273
|
+
? id.typeAnnotation.typeAnnotation.end
|
|
274
|
+
: decl.init.typeArguments
|
|
275
|
+
? decl.init.typeArguments.end - 1
|
|
276
|
+
: -1;
|
|
277
|
+
const first = names[0];
|
|
278
|
+
const firstRange = [elements[0].start, elements[0].end];
|
|
279
|
+
const dropLastSemi = ch === ';';
|
|
280
|
+
if (opts.typedCells) {
|
|
281
|
+
const cellName = g.cellCount === 0 ? '__cell' : `__cell${g.cellCount}`;
|
|
282
|
+
g.cellCount++;
|
|
283
|
+
// The cell holds a Tracked<T>, NOT a T — leave its type to inference
|
|
284
|
+
// from `track<T>(init)` so raw-cell aliases stay correctly typed too.
|
|
285
|
+
// Annotating it with the value annotation made `&[v, cell]` emit
|
|
286
|
+
// `cell: T = <Tracked>` (unsound), and broke passing `cell` to APIs
|
|
287
|
+
// expecting `Tracked<unknown>` (e.g. useFetch `into`).
|
|
288
|
+
g.add(`const ${cellName} = `);
|
|
289
|
+
g.add(initText, decl.init.start, decl.init.end);
|
|
290
|
+
g.add(';');
|
|
291
|
+
// First binding: the VALUE read from the cell.
|
|
292
|
+
g.add(' let ');
|
|
293
|
+
g.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
|
|
294
|
+
if (annotation) {
|
|
295
|
+
g.add(': ');
|
|
296
|
+
g.add(annotation, annStart, annEnd);
|
|
297
|
+
}
|
|
298
|
+
g.add(` = ${cellName}.get();`);
|
|
299
|
+
// Raw-cell aliases: inferred Tracked<T>, never the value annotation.
|
|
300
|
+
for (let n = 1; n < names.length; n++) {
|
|
301
|
+
g.add(' let ');
|
|
302
|
+
g.add(names[n], elements[n].start, elements[n].end, REACTIVE_DATA);
|
|
303
|
+
g.add(` = ${cellName};`);
|
|
304
|
+
}
|
|
305
|
+
if (dropLastSemi)
|
|
306
|
+
g.code = g.code.slice(0, -1);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
if (annotation) {
|
|
310
|
+
g.add('let ');
|
|
311
|
+
g.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
|
|
312
|
+
g.add(': ');
|
|
313
|
+
g.add(annotation, annStart, annEnd);
|
|
314
|
+
g.add(' = (');
|
|
315
|
+
g.add(initText, decl.init.start, decl.init.end);
|
|
316
|
+
g.add(' as unknown as ');
|
|
317
|
+
g.add(annotation, annStart, annEnd);
|
|
318
|
+
g.add(');');
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
g.add('let ');
|
|
322
|
+
g.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
|
|
323
|
+
g.add(': any = ');
|
|
324
|
+
g.add(initText, decl.init.start, decl.init.end);
|
|
325
|
+
g.add(';');
|
|
326
|
+
}
|
|
327
|
+
for (let n = 1; n < names.length; n++) {
|
|
328
|
+
g.add(' let ');
|
|
329
|
+
g.add(names[n], elements[n].start, elements[n].end, REACTIVE_DATA);
|
|
330
|
+
g.add(': any = ');
|
|
331
|
+
g.add(first, firstRange[0], firstRange[1]);
|
|
332
|
+
g.add(';');
|
|
333
|
+
if (n === names.length - 1 && dropLastSemi)
|
|
334
|
+
g.code = g.code.slice(0, -1);
|
|
335
|
+
}
|
|
336
|
+
if (names.length === 1 && dropLastSemi)
|
|
337
|
+
g.code = g.code.slice(0, -1);
|
|
338
|
+
}
|
|
339
|
+
prevEnd = decl.end;
|
|
340
|
+
}
|
|
341
|
+
if (!isLast)
|
|
342
|
+
g.addRaw(';');
|
|
343
|
+
}
|
|
344
|
+
function emitBlock(g, source, body, indent, opts) {
|
|
187
345
|
const stmts = body && body.type === 'BlockStatement'
|
|
188
346
|
? body.body
|
|
189
347
|
: body ? (Array.isArray(body) ? body : [body]) : [];
|
|
190
|
-
|
|
191
|
-
|
|
348
|
+
g.addRaw('{\n');
|
|
349
|
+
emitBody(g, source, stmts, indent + ' ', opts);
|
|
350
|
+
g.addRaw('\n');
|
|
351
|
+
g.add(indent);
|
|
352
|
+
g.addRaw('}');
|
|
192
353
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
354
|
+
function emitJSXAttr(g, source, attr) {
|
|
355
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
356
|
+
g.add(source.slice(attr.start, attr.end), attr.start, attr.end);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const name = attr.name;
|
|
360
|
+
g.add(source.slice(name.start, name.end), name.start, name.end);
|
|
361
|
+
const v = attr.value;
|
|
362
|
+
if (!v)
|
|
363
|
+
return;
|
|
364
|
+
g.add(source.slice(name.end, v.start), name.end, v.start);
|
|
365
|
+
if (v.type === 'JSXExpressionContainer') {
|
|
366
|
+
g.add(source.slice(v.start, v.expression.start), v.start, v.expression.start);
|
|
367
|
+
g.add(source.slice(v.expression.start, v.expression.end), v.expression.start, v.expression.end);
|
|
368
|
+
g.add(source.slice(v.expression.end, v.end), v.expression.end, v.end);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
g.add(source.slice(v.start, v.end), v.start, v.end);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function emitJSXElement(g, source, el, opts) {
|
|
375
|
+
const op = el.openingElement;
|
|
376
|
+
const name = op.name;
|
|
377
|
+
let prev;
|
|
378
|
+
if (name) {
|
|
379
|
+
g.add(source.slice(op.start, name.start), op.start, name.start);
|
|
380
|
+
g.add(source.slice(name.start, name.end), name.start, name.end);
|
|
381
|
+
prev = name.end;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
prev = op.start + 1;
|
|
385
|
+
}
|
|
386
|
+
for (const attr of op.attributes ?? []) {
|
|
387
|
+
g.add(source.slice(prev, attr.start), prev, attr.start);
|
|
388
|
+
emitJSXAttr(g, source, attr);
|
|
389
|
+
prev = attr.end;
|
|
390
|
+
}
|
|
391
|
+
const tail = source.slice(prev, op.end);
|
|
392
|
+
if (op.selfClosing && !tail.includes('/')) {
|
|
393
|
+
const gt = tail.lastIndexOf('>');
|
|
394
|
+
const head = gt === -1 ? tail : tail.slice(0, gt);
|
|
395
|
+
if (head)
|
|
396
|
+
g.add(head, prev, prev + head.length);
|
|
397
|
+
g.addRaw('/>');
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
g.add(tail, prev, op.end);
|
|
401
|
+
}
|
|
402
|
+
if (op.selfClosing)
|
|
403
|
+
return;
|
|
404
|
+
emitJSXChildren(g, source, el.children ?? [], opts);
|
|
405
|
+
if (el.closingElement) {
|
|
406
|
+
g.add(source.slice(el.closingElement.start, el.closingElement.end), el.closingElement.start, el.closingElement.end);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function emitJSXExprBody(g, source, expr, opts) {
|
|
410
|
+
if (expr.type === 'JSXElement')
|
|
411
|
+
return emitJSXElement(g, source, expr, opts);
|
|
412
|
+
if (expr.type === 'JSXFragment')
|
|
413
|
+
return emitJSXFragment(g, source, expr, opts);
|
|
414
|
+
g.add(source.slice(expr.start, expr.end), expr.start, expr.end);
|
|
415
|
+
}
|
|
416
|
+
function emitJSXFragment(g, source, frag, opts) {
|
|
417
|
+
g.addRaw('<>');
|
|
418
|
+
emitJSXChildren(g, source, frag.children ?? [], opts);
|
|
419
|
+
g.addRaw('</>');
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Emits the `.map(...)` scaffolding for a `for (... of ...)` / `for (... in
|
|
423
|
+
* ...)` clause inside JSX text, mapping the array/object expression to its
|
|
424
|
+
* source range. `clauseStart` is the collapsed-text offset of the clause's
|
|
425
|
+
* first character (inside `text`) and `map` translates collapsed offsets
|
|
426
|
+
* back to raw source offsets.
|
|
427
|
+
*/
|
|
428
|
+
function emitForClauseMap(g, source, child, text, map, clauseStart, bodyEmit, emptyEmit) {
|
|
429
|
+
const clauseEnd = findBalancedEnd(text, clauseStart - 1);
|
|
430
|
+
const clause = text.slice(clauseStart, clauseEnd);
|
|
431
|
+
const ofIdx = findTopLevelSep(clause, 'of');
|
|
432
|
+
const inIdx = ofIdx === -1 ? findTopLevelSep(clause, 'in') : -1;
|
|
433
|
+
if (ofIdx === -1 && inIdx === -1)
|
|
434
|
+
return false;
|
|
435
|
+
const sepIdx = ofIdx === -1 ? inIdx : ofIdx;
|
|
436
|
+
const lhs = clause.slice(0, sepIdx);
|
|
437
|
+
const rhs = clause.slice(sepIdx + 2);
|
|
438
|
+
const arrExpr = rhs.trim();
|
|
439
|
+
const arrStartCollapsed = clauseStart + sepIdx + 2 + leadingWsLen(rhs);
|
|
440
|
+
const arrEndCollapsed = clauseStart + sepIdx + 2 + rhs.length - trailingWsLen(rhs);
|
|
441
|
+
const arrStart = arrStartCollapsed < map.length ? child.start + map[arrStartCollapsed] : child.start;
|
|
442
|
+
const arrEnd = arrEndCollapsed > 0 && arrEndCollapsed - 1 < map.length
|
|
443
|
+
? child.start + map[arrEndCollapsed - 1] + 1
|
|
444
|
+
: child.start;
|
|
445
|
+
const itemVar = stripDeclKeyword(lhs).trim();
|
|
446
|
+
if (ofIdx !== -1) {
|
|
447
|
+
if (emptyEmit) {
|
|
448
|
+
g.addRaw('{ (() => { const _v = ');
|
|
449
|
+
g.add(arrExpr, arrStart, arrEnd);
|
|
450
|
+
g.addRaw('; return _v.length === 0 ? (');
|
|
451
|
+
emptyEmit();
|
|
452
|
+
g.addRaw(') : _v.map((');
|
|
453
|
+
g.addRaw(itemVar);
|
|
454
|
+
g.addRaw(': (typeof _v)[number]) => (');
|
|
455
|
+
bodyEmit();
|
|
456
|
+
g.addRaw(')); })() }');
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
g.addRaw('{ ');
|
|
460
|
+
g.add(arrExpr, arrStart, arrEnd);
|
|
461
|
+
g.addRaw('.map((');
|
|
462
|
+
g.addRaw(itemVar);
|
|
463
|
+
g.addRaw(': (typeof ');
|
|
464
|
+
g.add(arrExpr, arrStart, arrEnd);
|
|
465
|
+
g.addRaw(')[number]) => (');
|
|
466
|
+
bodyEmit();
|
|
467
|
+
g.addRaw(')) }');
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
g.addRaw('{ Object.keys(');
|
|
472
|
+
g.add(arrExpr, arrStart, arrEnd);
|
|
473
|
+
g.addRaw(').map((');
|
|
474
|
+
g.addRaw(itemVar);
|
|
475
|
+
g.addRaw(') => (');
|
|
476
|
+
bodyEmit();
|
|
477
|
+
g.addRaw(')) }');
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
function emitJSXChildren(g, source, children, opts) {
|
|
195
482
|
let i = 0;
|
|
196
483
|
let forPending = false;
|
|
197
484
|
while (i < children.length) {
|
|
@@ -199,7 +486,7 @@ function tsxEmitJSXChildren(source, children) {
|
|
|
199
486
|
if (child.type !== 'JSXText')
|
|
200
487
|
forPending = false;
|
|
201
488
|
if (child.type === 'JSXText') {
|
|
202
|
-
const text =
|
|
489
|
+
const { text, map } = collapseWithMap(child.value);
|
|
203
490
|
const trimmed = text.trim();
|
|
204
491
|
if (!trimmed || trimmed.startsWith('//')) {
|
|
205
492
|
i++;
|
|
@@ -210,56 +497,42 @@ function tsxEmitJSXChildren(source, children) {
|
|
|
210
497
|
if (forDecl !== null && nextChild !== undefined && (nextChild.type === 'JSXExpressionContainer' ||
|
|
211
498
|
nextChild.type === 'JSXElement' ||
|
|
212
499
|
nextChild.type === 'JSXFragment')) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
500
|
+
const trimmedStartCollapsed = leadingWsLen(text);
|
|
501
|
+
const parenIdx = skipWhitespace(text, trimmedStartCollapsed + 3);
|
|
502
|
+
if (text[parenIdx] === '(') {
|
|
503
|
+
const clauseStartCollapsed = parenIdx + 1;
|
|
504
|
+
let consumed = 2;
|
|
505
|
+
let emptyEmit = null;
|
|
506
|
+
const emptyTextNode = children[i + 2];
|
|
507
|
+
const emptyContainer = children[i + 3];
|
|
508
|
+
if (emptyTextNode && emptyTextNode.type === 'JSXText' &&
|
|
509
|
+
['#empty', 'empty'].includes(emptyTextNode.value.trim()) &&
|
|
510
|
+
emptyContainer && emptyContainer.type === 'JSXExpressionContainer' &&
|
|
511
|
+
emptyContainer.expression.type !== 'JSXEmptyExpression') {
|
|
512
|
+
consumed = 4;
|
|
513
|
+
emptyEmit = () => emitJSXExprBody(g, source, emptyContainer.expression, opts);
|
|
220
514
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (emptyTextNode && emptyTextNode.type === 'JSXText' &&
|
|
234
|
-
['#empty', 'empty'].includes(emptyTextNode.value.trim()) &&
|
|
235
|
-
emptyContainer && emptyContainer.type === 'JSXExpressionContainer' &&
|
|
236
|
-
emptyContainer.expression.type !== 'JSXEmptyExpression') {
|
|
237
|
-
emptyText = tsxEmitJSXExprBody(source, emptyContainer.expression);
|
|
238
|
-
consumed = 4;
|
|
239
|
-
}
|
|
240
|
-
const ofParts = splitTopLevel(forDecl, 'of');
|
|
241
|
-
if (ofParts.length === 2) {
|
|
242
|
-
const itemVar = stripDeclKeyword(ofParts[0]).trim();
|
|
243
|
-
const arrExpr = ofParts[1].trim();
|
|
244
|
-
const map = `{ ${arrExpr}.map((${itemVar}: (typeof ${arrExpr})[number]) => (${bodyText})) }`;
|
|
245
|
-
if (emptyText) {
|
|
246
|
-
parts.push(`{ (() => { const _v = ${arrExpr}; return _v.length === 0 ? (${emptyText}) : _v.map((${itemVar}: (typeof _v)[number]) => (${bodyText})); })() }`);
|
|
515
|
+
let matched = false;
|
|
516
|
+
if (nextChild.type === 'JSXExpressionContainer') {
|
|
517
|
+
const exprNode = nextChild.expression;
|
|
518
|
+
if (exprNode.type === 'JSXEmptyExpression') {
|
|
519
|
+
g.add(text, child.start, child.end);
|
|
520
|
+
i++;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
matched = emitForClauseMap(g, source, child, text, map, clauseStartCollapsed, () => emitJSXExprBody(g, source, exprNode, opts), emptyEmit);
|
|
524
|
+
}
|
|
525
|
+
else if (nextChild.type === 'JSXFragment') {
|
|
526
|
+
matched = emitForClauseMap(g, source, child, text, map, clauseStartCollapsed, () => emitJSXFragment(g, source, nextChild, opts), emptyEmit);
|
|
247
527
|
}
|
|
248
528
|
else {
|
|
249
|
-
|
|
529
|
+
matched = emitForClauseMap(g, source, child, text, map, clauseStartCollapsed, () => emitJSXElement(g, source, nextChild, opts), emptyEmit);
|
|
530
|
+
}
|
|
531
|
+
if (matched) {
|
|
532
|
+
i += consumed;
|
|
533
|
+
forPending = true;
|
|
534
|
+
continue;
|
|
250
535
|
}
|
|
251
|
-
i += consumed;
|
|
252
|
-
forPending = true;
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const inParts = splitTopLevel(forDecl, 'in');
|
|
256
|
-
if (inParts.length === 2) {
|
|
257
|
-
const itemVar = stripDeclKeyword(inParts[0]).trim();
|
|
258
|
-
const objExpr = inParts[1].trim();
|
|
259
|
-
parts.push(`{ Object.keys(${objExpr}).map((${itemVar}) => (${bodyText})) }`);
|
|
260
|
-
i += consumed;
|
|
261
|
-
forPending = true;
|
|
262
|
-
continue;
|
|
263
536
|
}
|
|
264
537
|
}
|
|
265
538
|
if (forPending && trimmed === '}') {
|
|
@@ -268,7 +541,7 @@ function tsxEmitJSXChildren(source, children) {
|
|
|
268
541
|
continue;
|
|
269
542
|
}
|
|
270
543
|
forPending = false;
|
|
271
|
-
|
|
544
|
+
g.add(text, child.start, child.end);
|
|
272
545
|
i++;
|
|
273
546
|
}
|
|
274
547
|
else if (child.type === 'JSXExpressionContainer') {
|
|
@@ -276,165 +549,227 @@ function tsxEmitJSXChildren(source, children) {
|
|
|
276
549
|
i++;
|
|
277
550
|
continue;
|
|
278
551
|
}
|
|
279
|
-
|
|
552
|
+
g.addRaw('{');
|
|
553
|
+
g.add(source.slice(child.expression.start, child.expression.end), child.expression.start, child.expression.end);
|
|
554
|
+
g.addRaw('}');
|
|
280
555
|
i++;
|
|
281
556
|
}
|
|
282
557
|
else if (child.type === 'JSXElement') {
|
|
283
558
|
if (getJSXTagName(child) === 'style') {
|
|
559
|
+
const openEnd = child.openingElement?.end ?? child.start;
|
|
560
|
+
const closeStart = child.closingElement?.start ?? child.end;
|
|
561
|
+
g.recordStyle(child.start, child.end, source.slice(openEnd, closeStart));
|
|
284
562
|
i++;
|
|
285
563
|
continue;
|
|
286
564
|
}
|
|
287
|
-
|
|
565
|
+
emitJSXElement(g, source, child, opts);
|
|
288
566
|
i++;
|
|
289
567
|
}
|
|
290
568
|
else if (child.type === 'JSXFragment') {
|
|
291
|
-
|
|
569
|
+
emitJSXFragment(g, source, child, opts);
|
|
292
570
|
i++;
|
|
293
571
|
}
|
|
294
572
|
else {
|
|
295
573
|
i++;
|
|
296
574
|
}
|
|
297
575
|
}
|
|
298
|
-
return parts.join('');
|
|
299
576
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
function tsxEmitJSXElement(source, el) {
|
|
308
|
-
const opening = source.slice(el.openingElement.start, el.openingElement.end);
|
|
309
|
-
if (el.openingElement.selfClosing)
|
|
310
|
-
return opening;
|
|
311
|
-
const inner = tsxEmitJSXChildren(source, el.children ?? []);
|
|
312
|
-
const closing = el.closingElement ? source.slice(el.closingElement.start, el.closingElement.end) : '';
|
|
313
|
-
return opening + inner + closing;
|
|
314
|
-
}
|
|
315
|
-
function tsxEmitJSXFragment(source, frag) {
|
|
316
|
-
const inner = tsxEmitJSXChildren(source, frag.children ?? []);
|
|
317
|
-
return '<>' + inner + '</>';
|
|
318
|
-
}
|
|
319
|
-
function tsxEmitIf(source, stmt, indent) {
|
|
320
|
-
const cond = source.slice(stmt.test.start, stmt.test.end);
|
|
321
|
-
let out = `${indent}if (${cond}) ${tsxBlock(source, stmt.consequent, indent)}`;
|
|
577
|
+
function emitIf(g, source, stmt, indent, opts) {
|
|
578
|
+
g.add(indent);
|
|
579
|
+
g.addRaw('if (');
|
|
580
|
+
g.add(source.slice(stmt.test.start, stmt.test.end), stmt.test.start, stmt.test.end);
|
|
581
|
+
g.addRaw(') ');
|
|
582
|
+
emitBlock(g, source, stmt.consequent, indent, opts);
|
|
322
583
|
if (stmt.alternate) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
: tsxBlock(source, [stmt.alternate], indent);
|
|
326
|
-
out += ` else ${altBlock}`;
|
|
584
|
+
g.addRaw(' else ');
|
|
585
|
+
emitBlock(g, source, stmt.alternate, indent, opts);
|
|
327
586
|
}
|
|
328
|
-
return out;
|
|
329
587
|
}
|
|
330
|
-
function
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
588
|
+
function emitForOf(g, source, stmt, indent, opts) {
|
|
589
|
+
g.add(indent);
|
|
590
|
+
g.addRaw('for (');
|
|
591
|
+
g.add(source.slice(stmt.left.start, stmt.left.end), stmt.left.start, stmt.left.end);
|
|
592
|
+
g.addRaw(' of ');
|
|
593
|
+
g.add(source.slice(stmt.right.start, stmt.right.end), stmt.right.start, stmt.right.end);
|
|
594
|
+
g.addRaw(') ');
|
|
595
|
+
emitBlock(g, source, stmt.body, indent, opts);
|
|
596
|
+
}
|
|
597
|
+
function emitForIn(g, source, stmt, indent, opts) {
|
|
598
|
+
g.add(indent);
|
|
599
|
+
g.addRaw('for (');
|
|
600
|
+
g.add(source.slice(stmt.left.start, stmt.left.end), stmt.left.start, stmt.left.end);
|
|
601
|
+
g.addRaw(' in ');
|
|
602
|
+
g.add(source.slice(stmt.right.start, stmt.right.end), stmt.right.start, stmt.right.end);
|
|
603
|
+
g.addRaw(') ');
|
|
604
|
+
emitBlock(g, source, stmt.body, indent, opts);
|
|
605
|
+
}
|
|
606
|
+
function emitFor(g, source, stmt, indent, opts) {
|
|
607
|
+
g.add(indent);
|
|
608
|
+
g.addRaw('for (');
|
|
609
|
+
if (stmt.init)
|
|
610
|
+
g.add(source.slice(stmt.init.start, stmt.init.end), stmt.init.start, stmt.init.end);
|
|
611
|
+
g.addRaw('; ');
|
|
612
|
+
if (stmt.test)
|
|
613
|
+
g.add(source.slice(stmt.test.start, stmt.test.end), stmt.test.start, stmt.test.end);
|
|
614
|
+
g.addRaw('; ');
|
|
615
|
+
if (stmt.update)
|
|
616
|
+
g.add(source.slice(stmt.update.start, stmt.update.end), stmt.update.start, stmt.update.end);
|
|
617
|
+
g.addRaw(') ');
|
|
618
|
+
emitBlock(g, source, stmt.body, indent, opts);
|
|
619
|
+
}
|
|
620
|
+
function emitWhile(g, source, stmt, indent, opts) {
|
|
621
|
+
g.add(indent);
|
|
622
|
+
g.addRaw('while (');
|
|
623
|
+
g.add(source.slice(stmt.test.start, stmt.test.end), stmt.test.start, stmt.test.end);
|
|
624
|
+
g.addRaw(') ');
|
|
625
|
+
emitBlock(g, source, stmt.body, indent, opts);
|
|
626
|
+
}
|
|
627
|
+
function emitDoWhile(g, source, stmt, indent, opts) {
|
|
628
|
+
g.add(indent);
|
|
629
|
+
g.addRaw('do ');
|
|
630
|
+
emitBlock(g, source, stmt.body, indent, opts);
|
|
631
|
+
g.addRaw(' while (');
|
|
632
|
+
g.add(source.slice(stmt.test.start, stmt.test.end), stmt.test.start, stmt.test.end);
|
|
633
|
+
g.addRaw(');');
|
|
634
|
+
}
|
|
635
|
+
function emitSwitch(g, source, stmt, indent, opts) {
|
|
636
|
+
g.add(indent);
|
|
637
|
+
g.addRaw('switch (');
|
|
638
|
+
g.add(source.slice(stmt.discriminant.start, stmt.discriminant.end), stmt.discriminant.start, stmt.discriminant.end);
|
|
639
|
+
g.addRaw(') {');
|
|
640
|
+
let firstCase = true;
|
|
357
641
|
for (const c of stmt.cases ?? []) {
|
|
642
|
+
g.addRaw(firstCase ? '\n' : '\n');
|
|
643
|
+
firstCase = false;
|
|
644
|
+
g.add(indent);
|
|
645
|
+
g.addRaw(' ');
|
|
358
646
|
if (c.test) {
|
|
359
|
-
|
|
647
|
+
g.addRaw('case ');
|
|
648
|
+
g.add(source.slice(c.test.start, c.test.end), c.test.start, c.test.end);
|
|
649
|
+
g.addRaw(':');
|
|
360
650
|
}
|
|
361
651
|
else {
|
|
362
|
-
|
|
652
|
+
g.addRaw('default:');
|
|
363
653
|
}
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
654
|
+
const before = g.code.length;
|
|
655
|
+
emitBody(g, source, c.consequent, indent + ' ', opts);
|
|
656
|
+
if (g.code.length > before)
|
|
657
|
+
g.addRaw('\n');
|
|
367
658
|
}
|
|
368
|
-
|
|
369
|
-
|
|
659
|
+
g.addRaw('\n');
|
|
660
|
+
g.add(indent);
|
|
661
|
+
g.addRaw('}');
|
|
370
662
|
}
|
|
371
|
-
function
|
|
372
|
-
|
|
663
|
+
function emitTry(g, source, stmt, indent, opts) {
|
|
664
|
+
g.add(indent);
|
|
665
|
+
g.addRaw('try ');
|
|
666
|
+
emitBlock(g, source, stmt.block, indent, opts);
|
|
373
667
|
if (stmt.handler) {
|
|
374
|
-
|
|
375
|
-
|
|
668
|
+
g.addRaw(' catch');
|
|
669
|
+
if (stmt.handler.param) {
|
|
670
|
+
g.addRaw(' (');
|
|
671
|
+
g.add(source.slice(stmt.handler.param.start, stmt.handler.param.end), stmt.handler.param.start, stmt.handler.param.end);
|
|
672
|
+
g.addRaw(')');
|
|
673
|
+
}
|
|
674
|
+
g.addRaw(' ');
|
|
675
|
+
emitBlock(g, source, stmt.handler.body, indent, opts);
|
|
376
676
|
}
|
|
377
677
|
if (stmt.finalizer) {
|
|
378
|
-
|
|
678
|
+
g.addRaw(' finally ');
|
|
679
|
+
emitBlock(g, source, stmt.finalizer, indent, opts);
|
|
379
680
|
}
|
|
380
|
-
return out;
|
|
381
681
|
}
|
|
382
|
-
function
|
|
682
|
+
function emitPlainStatement(g, source, stmt, indent, isLast) {
|
|
683
|
+
let text = stripTrailingSemicolons(source.slice(stmt.start, stmt.end).trimEnd());
|
|
684
|
+
if (text === '')
|
|
685
|
+
return;
|
|
686
|
+
g.add(indent);
|
|
687
|
+
g.add(text, stmt.start, stmt.end);
|
|
688
|
+
if (!text.endsWith('}') && !isLast)
|
|
689
|
+
g.addRaw(';');
|
|
690
|
+
}
|
|
691
|
+
function emitStatement(g, source, stmt, indent, isLast, opts) {
|
|
383
692
|
switch (stmt.type) {
|
|
384
693
|
case 'JSXElement':
|
|
385
|
-
if (getJSXTagName(stmt) === 'style')
|
|
386
|
-
|
|
387
|
-
|
|
694
|
+
if (getJSXTagName(stmt) === 'style') {
|
|
695
|
+
const openEnd = stmt.openingElement?.end ?? stmt.start;
|
|
696
|
+
const closeStart = stmt.closingElement?.start ?? stmt.end;
|
|
697
|
+
g.recordStyle(stmt.start, stmt.end, source.slice(openEnd, closeStart));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
g.add(indent);
|
|
701
|
+
emitJSXElement(g, source, stmt, opts);
|
|
702
|
+
if (!isLast)
|
|
703
|
+
g.addRaw(';');
|
|
704
|
+
return;
|
|
388
705
|
case 'JSXFragment':
|
|
389
|
-
|
|
706
|
+
g.add(indent);
|
|
707
|
+
emitJSXFragment(g, source, stmt, opts);
|
|
708
|
+
if (!isLast)
|
|
709
|
+
g.addRaw(';');
|
|
710
|
+
return;
|
|
390
711
|
case 'JSXExpressionContainer': {
|
|
391
712
|
if (stmt.expression.type === 'JSXEmptyExpression')
|
|
392
|
-
return
|
|
393
|
-
|
|
394
|
-
|
|
713
|
+
return;
|
|
714
|
+
g.add(indent);
|
|
715
|
+
g.addRaw('{');
|
|
716
|
+
g.add(source.slice(stmt.expression.start, stmt.expression.end), stmt.expression.start, stmt.expression.end);
|
|
717
|
+
g.addRaw('}');
|
|
718
|
+
if (!isLast)
|
|
719
|
+
g.addRaw(';');
|
|
720
|
+
return;
|
|
395
721
|
}
|
|
396
722
|
case 'VeskBlock': {
|
|
397
723
|
if (stmt.tag === 'empty')
|
|
398
|
-
return
|
|
399
|
-
const lines = [];
|
|
724
|
+
return;
|
|
400
725
|
const innerStmts = stmt.body ?? [];
|
|
726
|
+
let firstLine = true;
|
|
401
727
|
for (let i = 0; i < innerStmts.length; i++) {
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
728
|
+
const before = g.code.length;
|
|
729
|
+
emitStatement(g, source, innerStmts[i], indent, isLast && i === innerStmts.length - 1, opts);
|
|
730
|
+
if (g.code.length > before) {
|
|
731
|
+
if (!firstLine)
|
|
732
|
+
g.addRaw('\n');
|
|
733
|
+
firstLine = false;
|
|
734
|
+
}
|
|
405
735
|
}
|
|
406
|
-
return
|
|
736
|
+
return;
|
|
407
737
|
}
|
|
738
|
+
case 'VariableDeclaration':
|
|
739
|
+
if (isTrackDeclStatement(stmt)) {
|
|
740
|
+
emitTrackDeclStatement(g, source, stmt, indent, isLast, opts);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
return emitPlainStatement(g, source, stmt, indent, isLast);
|
|
408
744
|
case 'IfStatement':
|
|
409
|
-
return
|
|
745
|
+
return emitIf(g, source, stmt, indent, opts);
|
|
410
746
|
case 'ForOfStatement':
|
|
411
|
-
return
|
|
747
|
+
return emitForOf(g, source, stmt, indent, opts);
|
|
412
748
|
case 'ForInStatement':
|
|
413
|
-
return
|
|
749
|
+
return emitForIn(g, source, stmt, indent, opts);
|
|
414
750
|
case 'ForStatement':
|
|
415
|
-
return
|
|
751
|
+
return emitFor(g, source, stmt, indent, opts);
|
|
416
752
|
case 'WhileStatement':
|
|
417
|
-
return
|
|
753
|
+
return emitWhile(g, source, stmt, indent, opts);
|
|
418
754
|
case 'DoWhileStatement':
|
|
419
|
-
return
|
|
755
|
+
return emitDoWhile(g, source, stmt, indent, opts);
|
|
420
756
|
case 'SwitchStatement':
|
|
421
|
-
return
|
|
757
|
+
return emitSwitch(g, source, stmt, indent, opts);
|
|
422
758
|
case 'TryStatement':
|
|
423
|
-
return
|
|
759
|
+
return emitTry(g, source, stmt, indent, opts);
|
|
424
760
|
case 'LabeledStatement': {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (text === '')
|
|
431
|
-
return '';
|
|
432
|
-
return indent + text + (text.endsWith('}') || isLast ? '' : ';');
|
|
761
|
+
g.add(indent);
|
|
762
|
+
g.add(stmt.label.name, stmt.label.start, stmt.label.end);
|
|
763
|
+
g.addRaw(':\n');
|
|
764
|
+
emitStatement(g, source, stmt.body, indent, isLast, opts);
|
|
765
|
+
return;
|
|
433
766
|
}
|
|
767
|
+
default:
|
|
768
|
+
return emitPlainStatement(g, source, stmt, indent, isLast);
|
|
434
769
|
}
|
|
435
770
|
}
|
|
436
|
-
function
|
|
437
|
-
|
|
771
|
+
function emitBody(g, source, stmts, indent, opts) {
|
|
772
|
+
let firstLine = true;
|
|
438
773
|
let i = 0;
|
|
439
774
|
while (i < stmts.length) {
|
|
440
775
|
const stmt = stmts[i];
|
|
@@ -442,107 +777,254 @@ function tsxEmitBody(source, stmts, indent) {
|
|
|
442
777
|
if (stmt.type === 'ForOfStatement' &&
|
|
443
778
|
stmts[i + 1] && stmts[i + 1].type === 'VeskBlock' && stmts[i + 1].tag === 'empty') {
|
|
444
779
|
const empty = stmts[i + 1];
|
|
445
|
-
|
|
780
|
+
if (!firstLine)
|
|
781
|
+
g.addRaw('\n');
|
|
782
|
+
firstLine = false;
|
|
783
|
+
emitForOf(g, source, stmt, indent, opts);
|
|
446
784
|
const inner = empty.body ?? [];
|
|
447
785
|
if (inner.length > 0) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
786
|
+
g.addRaw('\n');
|
|
787
|
+
g.add(indent);
|
|
788
|
+
g.add(source.slice(stmt.right.start, stmt.right.end), stmt.right.start, stmt.right.end);
|
|
789
|
+
g.addRaw('.length === 0 && (() => {');
|
|
790
|
+
g.addRaw('\n');
|
|
791
|
+
emitBody(g, source, inner, indent + ' ', opts);
|
|
792
|
+
g.addRaw('\n');
|
|
793
|
+
g.add(indent);
|
|
794
|
+
g.addRaw('})();');
|
|
453
795
|
}
|
|
454
796
|
i += 2;
|
|
455
797
|
continue;
|
|
456
798
|
}
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
799
|
+
const before = g.code.length;
|
|
800
|
+
emitStatement(g, source, stmt, indent, isLast, opts);
|
|
801
|
+
if (g.code.length > before) {
|
|
802
|
+
if (!firstLine)
|
|
803
|
+
g.addRaw('\n');
|
|
804
|
+
firstLine = false;
|
|
805
|
+
}
|
|
460
806
|
i++;
|
|
461
807
|
}
|
|
462
|
-
return lines.join('\n');
|
|
463
808
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
809
|
+
/**
|
|
810
|
+
* Emits `source[start, end)` as a mapped chunk, skipping any `<style>`
|
|
811
|
+
* blocks (recorded as style regions). Used for expression-mode bodies and
|
|
812
|
+
* the gaps around statements/expressions.
|
|
813
|
+
*/
|
|
814
|
+
function emitChunkMapped(g, source, start, end) {
|
|
815
|
+
if (end <= start)
|
|
816
|
+
return;
|
|
817
|
+
let pos = start;
|
|
818
|
+
let i = start;
|
|
819
|
+
while (i < end) {
|
|
820
|
+
if (source[i] === '<' && source.slice(i, i + 6) === '<style') {
|
|
821
|
+
const next = i + 6;
|
|
822
|
+
if (next < end && (isWhitespaceChar(source[next]) || source[next] === '>' || source[next] === '/')) {
|
|
823
|
+
const openEnd = htmlTagEnd(source, i);
|
|
824
|
+
if (openEnd !== -1 && openEnd <= end) {
|
|
825
|
+
const closeIdx = source.indexOf('</style>', openEnd);
|
|
826
|
+
if (closeIdx !== -1 && closeIdx + 8 <= end) {
|
|
827
|
+
g.add(source.slice(pos, i), pos, i);
|
|
828
|
+
g.recordStyle(i, closeIdx + 8, source.slice(openEnd, closeIdx));
|
|
829
|
+
pos = closeIdx + 8;
|
|
830
|
+
i = closeIdx + 8;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
i++;
|
|
837
|
+
}
|
|
838
|
+
g.add(source.slice(pos, end), pos, end);
|
|
467
839
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
840
|
+
function emitReturn(g, source, stmt, opts) {
|
|
841
|
+
g.add(source.slice(stmt.start, stmt.start + 6), stmt.start, stmt.start + 6);
|
|
842
|
+
const arg = stmt.argument;
|
|
843
|
+
if (!arg) {
|
|
844
|
+
emitChunkMapped(g, source, stmt.start + 6, stmt.end);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
emitChunkMapped(g, source, stmt.start + 6, arg.start);
|
|
848
|
+
if (arg.type === 'JSXElement')
|
|
849
|
+
emitJSXElement(g, source, arg, opts);
|
|
850
|
+
else if (arg.type === 'JSXFragment')
|
|
851
|
+
emitJSXFragment(g, source, arg, opts);
|
|
852
|
+
else
|
|
853
|
+
emitChunkMapped(g, source, arg.start, arg.end);
|
|
854
|
+
emitChunkMapped(g, source, arg.end, stmt.end);
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Emits a statement-mode component body (bare JSX, control flow,
|
|
858
|
+
* guard-clause returns). The body text starts directly with the first
|
|
859
|
+
* statement (no leading whitespace) and the `{ ... }` wrapping is emitted
|
|
860
|
+
* by the caller.
|
|
861
|
+
*/
|
|
862
|
+
function emitBodyCore(g, source, stmts, opts) {
|
|
863
|
+
emitBody(g, source, stmts, '', opts);
|
|
864
|
+
}
|
|
865
|
+
/** Emits an expression-mode component body (`return <jsx>` or a bare expression). */
|
|
866
|
+
function emitExpressionBody(g, source, coreStart, coreEnd, stmts, opts) {
|
|
867
|
+
let pos = coreStart;
|
|
868
|
+
for (const stmt of stmts) {
|
|
869
|
+
if (!stmt)
|
|
479
870
|
continue;
|
|
871
|
+
emitChunkMapped(g, source, pos, stmt.start);
|
|
872
|
+
pos = stmt.start;
|
|
873
|
+
if (isTrackDeclStatement(stmt)) {
|
|
874
|
+
emitTrackDeclStatement(g, source, stmt, '', true, opts);
|
|
480
875
|
}
|
|
481
|
-
|
|
876
|
+
else if (stmt.type === 'ReturnStatement') {
|
|
877
|
+
emitReturn(g, source, stmt, opts);
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
emitChunkMapped(g, source, stmt.start, stmt.end);
|
|
881
|
+
}
|
|
882
|
+
pos = stmt.end;
|
|
482
883
|
}
|
|
483
|
-
|
|
884
|
+
emitChunkMapped(g, source, pos, coreEnd);
|
|
484
885
|
}
|
|
485
886
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* replaced by a single space.
|
|
887
|
+
* Emits `text` (covering source range [base, end)) with its first
|
|
888
|
+
* identifier split into its own mapping so hover/rename/definition land
|
|
889
|
+
* exactly on the component name.
|
|
490
890
|
*/
|
|
491
|
-
function
|
|
492
|
-
let
|
|
493
|
-
while (
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (text.slice(wordStart, end) === 'client') {
|
|
499
|
-
return text.slice(0, wordStart).trimEnd() + ' ';
|
|
891
|
+
function emitRestMapped(g, text, base, end) {
|
|
892
|
+
let i = 0;
|
|
893
|
+
while (i < text.length && !isIdentStart(text[i]))
|
|
894
|
+
i++;
|
|
895
|
+
if (i >= text.length) {
|
|
896
|
+
g.add(text, base, end);
|
|
897
|
+
return;
|
|
500
898
|
}
|
|
501
|
-
|
|
899
|
+
let j = i + 1;
|
|
900
|
+
while (j < text.length && isIdentChar(text[j]))
|
|
901
|
+
j++;
|
|
902
|
+
g.add(text.slice(0, i), base, base + i);
|
|
903
|
+
g.add(text.slice(i, j), base + i, base + j);
|
|
904
|
+
g.add(text.slice(j), base + j, end);
|
|
502
905
|
}
|
|
503
|
-
|
|
906
|
+
function emitComponentHeader(g, source, start, bodyStart) {
|
|
907
|
+
const h = source.slice(start, bodyStart);
|
|
908
|
+
const k = findFirstKeyword(h, 'component');
|
|
909
|
+
if (k === -1) {
|
|
910
|
+
const { text, end } = stripTrailingClientRange(h);
|
|
911
|
+
if (!text.includes('(')) {
|
|
912
|
+
const trimmed = text.trimEnd();
|
|
913
|
+
g.add(trimmed, start, start + end - (text.length - trimmed.length));
|
|
914
|
+
g.addRaw('() ');
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
g.add(text, start, start + end);
|
|
918
|
+
}
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
g.add(h.slice(0, k), start, start + k);
|
|
922
|
+
g.add('function', start + k, start + k + 'component'.length);
|
|
923
|
+
const restBase = start + k + 'component'.length;
|
|
924
|
+
const { text: rest, end } = stripTrailingClientRange(h.slice(k + 'component'.length));
|
|
925
|
+
const transformed = h.slice(0, k) + 'function' + rest;
|
|
926
|
+
if (!transformed.includes('(')) {
|
|
927
|
+
const trimmed = rest.trimEnd();
|
|
928
|
+
emitRestMapped(g, trimmed, restBase, restBase + end - (rest.length - trimmed.length));
|
|
929
|
+
g.addRaw('() ');
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
emitRestMapped(g, rest, restBase, restBase + end);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function emitComponent(g, source, node, start, opts) {
|
|
936
|
+
const body = node.body;
|
|
937
|
+
if (!body || body.start == null || body.end == null)
|
|
938
|
+
return;
|
|
939
|
+
emitComponentHeader(g, source, start, body.start);
|
|
940
|
+
const statementMode = isStatementMode(node);
|
|
941
|
+
const stmts = body.body ?? [];
|
|
942
|
+
if (statementMode) {
|
|
943
|
+
g.addRaw('{ ');
|
|
944
|
+
emitBodyCore(g, source, stmts, opts);
|
|
945
|
+
g.addRaw(' }');
|
|
946
|
+
}
|
|
947
|
+
else {
|
|
948
|
+
const raw = source.slice(body.start, body.end);
|
|
949
|
+
const rawLen = raw.length;
|
|
950
|
+
let s = 0;
|
|
951
|
+
let e = rawLen;
|
|
952
|
+
while (s < e && isWhitespaceChar(raw[s]))
|
|
953
|
+
s++;
|
|
954
|
+
while (e > s && isWhitespaceChar(raw[e - 1]))
|
|
955
|
+
e--;
|
|
956
|
+
const hasBraces = raw[s] === '{' && raw[e - 1] === '}';
|
|
957
|
+
let coreStart;
|
|
958
|
+
let coreEnd;
|
|
959
|
+
if (hasBraces) {
|
|
960
|
+
coreStart = body.start + s + 1;
|
|
961
|
+
coreEnd = body.start + e - 1;
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
coreStart = body.start + s;
|
|
965
|
+
coreEnd = body.start + e;
|
|
966
|
+
}
|
|
967
|
+
while (coreStart < coreEnd && isWhitespaceChar(source[coreStart]))
|
|
968
|
+
coreStart++;
|
|
969
|
+
while (coreEnd > coreStart && isWhitespaceChar(source[coreEnd - 1]))
|
|
970
|
+
coreEnd--;
|
|
971
|
+
if (hasBraces)
|
|
972
|
+
g.addRaw('{ ');
|
|
973
|
+
emitExpressionBody(g, source, coreStart, coreEnd, stmts, opts);
|
|
974
|
+
if (hasBraces)
|
|
975
|
+
g.addRaw(' }');
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function isModuleAst(ast) {
|
|
979
|
+
return (ast.body || []).some((s) => s.type === 'ImportDeclaration' ||
|
|
980
|
+
s.type === 'ExportNamedDeclaration' ||
|
|
981
|
+
s.type === 'ExportDefaultDeclaration' ||
|
|
982
|
+
s.type === 'ExportAllDeclaration');
|
|
983
|
+
}
|
|
984
|
+
export function compileVskCodegen(source, opts = {}) {
|
|
504
985
|
let ast;
|
|
505
986
|
try {
|
|
506
987
|
ast = parse(source);
|
|
507
988
|
}
|
|
508
|
-
catch {
|
|
509
|
-
|
|
989
|
+
catch (e) {
|
|
990
|
+
const pos = e.pos ?? 0;
|
|
991
|
+
return {
|
|
992
|
+
code: source,
|
|
993
|
+
mappings: [
|
|
994
|
+
{
|
|
995
|
+
sourceOffsets: [0],
|
|
996
|
+
generatedOffsets: [0],
|
|
997
|
+
lengths: [source.length],
|
|
998
|
+
generatedLengths: [source.length],
|
|
999
|
+
data: FULL_DATA,
|
|
1000
|
+
},
|
|
1001
|
+
],
|
|
1002
|
+
styleRegions: [],
|
|
1003
|
+
errors: [{ message: e.message, start: pos, end: pos + 1 }],
|
|
1004
|
+
};
|
|
510
1005
|
}
|
|
511
1006
|
const clean = blankForClauses(source, ast);
|
|
512
|
-
const
|
|
513
|
-
|
|
1007
|
+
const g = new VskGen();
|
|
1008
|
+
const components = getComponents(ast).sort((a, b) => a.start - b.start);
|
|
1009
|
+
let pos = 0;
|
|
1010
|
+
for (const { node, start } of components) {
|
|
514
1011
|
const body = node.body;
|
|
515
1012
|
if (!body || body.start == null || body.end == null)
|
|
516
1013
|
continue;
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
const statementMode = isStatementMode(node);
|
|
522
|
-
let wrapped;
|
|
523
|
-
if (statementMode) {
|
|
524
|
-
const bodyText = rewriteTrackDecls(tsxEmitBody(clean, body.body ?? [], ''));
|
|
525
|
-
wrapped = '{ ' + bodyText.trim() + ' }';
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
const raw = clean.slice(body.start, body.end);
|
|
529
|
-
const inner = raw.trim();
|
|
530
|
-
const hasBraces = inner.startsWith('{') && inner.endsWith('}');
|
|
531
|
-
const core = hasBraces ? inner.slice(1, -1) : inner;
|
|
532
|
-
const bodyText = rewriteTrackDecls(stripStyleBlocks(core));
|
|
533
|
-
wrapped = hasBraces ? '{ ' + bodyText.trim() + ' }' : bodyText;
|
|
534
|
-
}
|
|
535
|
-
edits.push({ start, end: body.end, text: transformed + wrapped });
|
|
1014
|
+
g.add(clean.slice(pos, start), pos, start);
|
|
1015
|
+
emitComponent(g, clean, node, start, opts);
|
|
1016
|
+
pos = body.end;
|
|
536
1017
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (isModule && containsIdentifier(out, 'Head') && !isIdentifierImported(out, 'Head')) {
|
|
543
|
-
return `declare const Head: (props: { children?: unknown }) => unknown;\n${out}`;
|
|
1018
|
+
g.add(clean.slice(pos), pos, source.length);
|
|
1019
|
+
let code = g.code;
|
|
1020
|
+
if (isModuleAst(ast) && containsIdentifier(code, 'Head') && !isIdentifierImported(code, 'Head')) {
|
|
1021
|
+
g.prepend('declare const Head: (props: { children?: Component }) => Component;\n');
|
|
1022
|
+
code = g.code;
|
|
544
1023
|
}
|
|
545
|
-
return
|
|
1024
|
+
return { code, mappings: g.mappings, styleRegions: g.styleRegions, errors: [] };
|
|
1025
|
+
}
|
|
1026
|
+
export function vskToTsx(source) {
|
|
1027
|
+
return compileVskCodegen(source).code;
|
|
546
1028
|
}
|
|
547
1029
|
function isTypeDecl(stmt) {
|
|
548
1030
|
return (stmt.type === 'TSInterfaceDeclaration' ||
|
|
@@ -585,7 +1067,7 @@ export function generateVskDts(source) {
|
|
|
585
1067
|
const hasType = propsType !== null && propsType !== undefined;
|
|
586
1068
|
const collision = typeDeclNames.has(typeName);
|
|
587
1069
|
const signature = hasType
|
|
588
|
-
? (collision ? propsType : typeName) + ' & { children?:
|
|
1070
|
+
? (collision ? propsType : typeName) + ' & { children?: Component }'
|
|
589
1071
|
: 'any';
|
|
590
1072
|
if (hasType && !collision) {
|
|
591
1073
|
lines.push(`export type ${typeName} = ${propsType};`, '');
|
|
@@ -594,10 +1076,10 @@ export function generateVskDts(source) {
|
|
|
594
1076
|
lines.push(`export type ${typeName} = any;`, '');
|
|
595
1077
|
}
|
|
596
1078
|
if (defaultExport) {
|
|
597
|
-
lines.push(`export default function ${name}(props: ${signature}):
|
|
1079
|
+
lines.push(`export default function ${name}(props: ${signature}): Component;`, '');
|
|
598
1080
|
}
|
|
599
1081
|
else {
|
|
600
|
-
lines.push(`export declare function ${name}(props: ${signature}):
|
|
1082
|
+
lines.push(`export declare function ${name}(props: ${signature}): Component;`, '');
|
|
601
1083
|
}
|
|
602
1084
|
}
|
|
603
1085
|
return lines.join('\n');
|