@vesk/compiler 0.2.8 → 0.2.9

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.
@@ -31,6 +31,18 @@ function isIdentStartCode(code) {
31
31
  function isIdentCharCode(code) {
32
32
  return isIdentStartCode(code) || (code >= 48 && code <= 57);
33
33
  }
34
+ function getQualifiedJSXName(object) {
35
+ if (!object)
36
+ return object;
37
+ if (object.type === 'JSXIdentifier')
38
+ return object.name;
39
+ if (object.type === 'JSXNamespacedName')
40
+ return object.namespace.name + ':' + object.name.name;
41
+ if (object.type === 'JSXMemberExpression') {
42
+ return getQualifiedJSXName(object.object) + '.' + getQualifiedJSXName(object.property);
43
+ }
44
+ return null;
45
+ }
34
46
  function looksLikeGenericArrowAt(input, pos) {
35
47
  let i = pos + 1;
36
48
  while (i < input.length && isWsChar(input.charCodeAt(i)))
@@ -114,6 +126,11 @@ function looksLikeTypeAssertionAt(input, pos) {
114
126
  function isWsChar(code) {
115
127
  return code === 32 || code === 9 || code === 10 || code === 13 || code === 12;
116
128
  }
129
+ function skipWs(input, i) {
130
+ while (i < input.length && isWsChar(input.charCodeAt(i)))
131
+ i++;
132
+ return i;
133
+ }
117
134
  function isStyleBoundary(code) {
118
135
  return isWsChar(code) || code === 62 || code === 47 || code === 123;
119
136
  }
@@ -148,6 +165,9 @@ export function VeskParserPlugin(config = {}) {
148
165
  #closeTagName = null;
149
166
  #jsxStartsStatement = false;
150
167
  #inTSTypeDecl = false;
168
+ #pendingChildStatement = false;
169
+ /** per-statement context-stack depths (LIFO — child statements may nest) */
170
+ #jsxChildDepthStack = [];
151
171
  constructor(options, input) {
152
172
  super(options, input);
153
173
  }
@@ -155,7 +175,292 @@ export function VeskParserPlugin(config = {}) {
155
175
  const ctx = this.curContext();
156
176
  return ctx && (ctx.token === '{' || ctx.token === 'function');
157
177
  }
178
+ #isJsxChildrenContext() {
179
+ const ctx = this.curContext();
180
+ return !!ctx && ctx.token === '<tag>...</tag>';
181
+ }
182
+ /**
183
+ * Determines whether the text starting at `this.pos` opens a
184
+ * statement-mode control-flow header (`if (`, `for (`, `while (`,
185
+ * `switch (`, `try {`, `do {`) among JSX children. Scans chars only —
186
+ * the paren body must balance and be followed by a `{` block.
187
+ */
188
+ #scansChildStatement() {
189
+ const input = this.input;
190
+ let i = this.pos;
191
+ while (i < input.length && isWsChar(input.charCodeAt(i)))
192
+ i++;
193
+ if (i >= input.length || !isIdentStartCode(input.charCodeAt(i)))
194
+ return false;
195
+ const wordStart = i;
196
+ while (i < input.length && isIdentCharCode(input.charCodeAt(i)))
197
+ i++;
198
+ const word = input.slice(wordStart, i);
199
+ // Declaration statements (`const`/`let`/`var …;`) are also valid
200
+ // statement-mode element children. Recognise them by a `=` of
201
+ // assignment plus either a top-level `;` or a newline at depth 0
202
+ // (ASI-style). Bail out (stay text) when a closing JSX tag `</` is
203
+ // seen before a terminator: a real element-child statement never
204
+ // contains its parent's closing tag, while prose text like
205
+ // `<p>const value = 5 apples</p>` does.
206
+ if (word === 'const' || word === 'let' || word === 'var') {
207
+ let depth = 0;
208
+ let seenAssign = false;
209
+ let q = i;
210
+ while (q < input.length) {
211
+ const c = input.charCodeAt(q);
212
+ if (c === 34 || c === 39 || c === 96) { // " ' `
213
+ const quote = c;
214
+ let tplDepth = 0;
215
+ q++;
216
+ while (q < input.length) {
217
+ const ch = input.charCodeAt(q);
218
+ if (ch === 92) {
219
+ q += 2;
220
+ continue;
221
+ }
222
+ if (quote === 96 && ch === 36 && input.charCodeAt(q + 1) === 123) {
223
+ tplDepth++;
224
+ q += 2;
225
+ continue;
226
+ }
227
+ if (quote === 96 && ch === 125 && tplDepth > 0) {
228
+ tplDepth--;
229
+ q++;
230
+ continue;
231
+ }
232
+ if (ch === quote && tplDepth === 0) {
233
+ q++;
234
+ break;
235
+ }
236
+ q++;
237
+ }
238
+ continue;
239
+ }
240
+ if (c === 60 && input.charCodeAt(q + 1) === 47)
241
+ return false; // '</' -> it's JSX text, not a statement
242
+ if (c === 40 || c === 91 || c === 123)
243
+ depth++;
244
+ else if (c === 41 || c === 93 || c === 125)
245
+ depth--;
246
+ else if (c === 61 && depth === 0) { // '='
247
+ const n2 = input.charCodeAt(q + 1);
248
+ const n1 = q > 0 ? input.charCodeAt(q - 1) : 0;
249
+ if (n2 !== 61 && n2 !== 62 && n1 !== 33 && n1 !== 60 && n1 !== 61)
250
+ seenAssign = true; // not ==, =>, !=, <=, >=
251
+ }
252
+ else if (c === 59 && depth === 0)
253
+ return true; // ';'
254
+ else if ((c === 10 || c === 13) && depth === 0 && seenAssign)
255
+ return true; // ASI at end of line
256
+ q++;
257
+ }
258
+ return false;
259
+ }
260
+ // Bare expression statements (`console.log(x)`, `alert('hi')`,
261
+ // `doSomething(args)`) are also valid statement-mode element children.
262
+ // They are ambiguous with prose, so only treat them as code when they
263
+ // read like a standalone line of code — not a sentence:
264
+ // * an identifier call chain (`console.log`, `foo?.bar`) with NO
265
+ // whitespace before the `(` (prose writes `word (`, code writes
266
+ // `word(`),
267
+ // * a balanced `(...)` call,
268
+ // * then a terminator — a top-level `;` or ASI newline — with no
269
+ // bare word after the `)` before it (`doSomething(x) then more`
270
+ // is prose; `doSomething(x)` on its own line is code).
271
+ // Bail out (stay text) when a closing JSX tag `</` is seen first, as
272
+ // a real child statement never contains its parent's closing tag.
273
+ if (isIdentStartCode(input.charCodeAt(wordStart))) {
274
+ let q = i; // position just after the first identifier
275
+ // member chain: `.foo` / `?.bar`, no surrounding whitespace
276
+ while (q < input.length) {
277
+ const c = input.charCodeAt(q);
278
+ if (c === 46 /* . */) {
279
+ q++;
280
+ }
281
+ else if (c === 63 /* ? */ && input.charCodeAt(q + 1) === 46) {
282
+ q += 2;
283
+ }
284
+ else
285
+ break;
286
+ if (q >= input.length || !isIdentStartCode(input.charCodeAt(q)))
287
+ return false;
288
+ q++;
289
+ while (q < input.length && isIdentCharCode(input.charCodeAt(q)))
290
+ q++;
291
+ }
292
+ // must be a call: `(` with no whitespace before it
293
+ if (input.charCodeAt(q) === 40 /* ( */) {
294
+ let depth = 0;
295
+ let callClosed = false;
296
+ let s = q;
297
+ while (s < input.length) {
298
+ const c = input.charCodeAt(s);
299
+ if (c === 34 || c === 39 || c === 96) { // " ' `
300
+ const quote = c;
301
+ let tplDepth = 0;
302
+ s++;
303
+ while (s < input.length) {
304
+ const ch = input.charCodeAt(s);
305
+ if (ch === 92) {
306
+ s += 2;
307
+ continue;
308
+ }
309
+ if (quote === 96 && ch === 36 && input.charCodeAt(s + 1) === 123) {
310
+ tplDepth++;
311
+ s += 2;
312
+ continue;
313
+ }
314
+ if (quote === 96 && ch === 125 && tplDepth > 0) {
315
+ tplDepth--;
316
+ s++;
317
+ continue;
318
+ }
319
+ if (ch === quote && tplDepth === 0) {
320
+ s++;
321
+ break;
322
+ }
323
+ s++;
324
+ }
325
+ continue;
326
+ }
327
+ if (c === 60 && input.charCodeAt(s + 1) === 47)
328
+ return false; // '</'
329
+ if (c === 40 || c === 91 || c === 123)
330
+ depth++;
331
+ else if (c === 41 || c === 93 || c === 125) {
332
+ depth--;
333
+ if (c === 41 && depth === 0) {
334
+ callClosed = true;
335
+ s++;
336
+ break;
337
+ }
338
+ }
339
+ s++;
340
+ }
341
+ if (!callClosed)
342
+ return false;
343
+ // After the call, walk forward treating each character in turn so
344
+ // we can tell "standalone statement ending in `;` / newline" from
345
+ // "trailing prose on the same line". Chained member calls
346
+ // (`.foo(...)`) and paren-groups are consumed; a bare word on the
347
+ // same line before a newline/`;` means the child is a sentence.
348
+ let d = 0;
349
+ for (;;) {
350
+ if (s >= input.length)
351
+ return false;
352
+ const c = input.charCodeAt(s);
353
+ if (c === 60 && input.charCodeAt(s + 1) === 47)
354
+ return false; // '</' -> prose
355
+ if (c === 34 || c === 39 || c === 96) { // " ' `
356
+ const quote = c;
357
+ let tplDepth = 0;
358
+ s++;
359
+ while (s < input.length) {
360
+ const ch = input.charCodeAt(s);
361
+ if (ch === 92) {
362
+ s += 2;
363
+ continue;
364
+ }
365
+ if (quote === 96 && ch === 36 && input.charCodeAt(s + 1) === 123) {
366
+ tplDepth++;
367
+ s += 2;
368
+ continue;
369
+ }
370
+ if (quote === 96 && ch === 125 && tplDepth > 0) {
371
+ tplDepth--;
372
+ s++;
373
+ continue;
374
+ }
375
+ if (ch === quote && tplDepth === 0) {
376
+ s++;
377
+ break;
378
+ }
379
+ s++;
380
+ }
381
+ continue;
382
+ }
383
+ if (c === 40 || c === 91 || c === 123)
384
+ d++;
385
+ else if (c === 41 || c === 93 || c === 125)
386
+ d--;
387
+ else if (c === 59 && d === 0)
388
+ return true; // ';' -> statement
389
+ else if ((c === 10 || c === 13) && d === 0)
390
+ return true; // ASI newline -> standalone
391
+ else if (c === 46) { // member access `.foo`; may lead to another call
392
+ s++;
393
+ while (s < input.length && isIdentCharCode(input.charCodeAt(s)))
394
+ s++;
395
+ continue;
396
+ }
397
+ else if (isIdentCharCode(c) || isIdentStartCode(c))
398
+ return false; // bare word -> prose
399
+ s++;
400
+ }
401
+ }
402
+ }
403
+ if (word !== 'if' && word !== 'for' && word !== 'while' && word !== 'switch' && word !== 'try' && word !== 'do')
404
+ return false;
405
+ let p = i;
406
+ while (p < input.length && isWsChar(input.charCodeAt(p)))
407
+ p++;
408
+ if (word === 'try' || word === 'do') {
409
+ return input.charCodeAt(p) === 123; // '{'
410
+ }
411
+ if (input.charCodeAt(p) !== 40)
412
+ return false; // '('
413
+ let depth = 1;
414
+ let q = p + 1;
415
+ while (q < input.length) {
416
+ const c = input.charCodeAt(q);
417
+ if (c === 34 || c === 39 || c === 96) {
418
+ const quote = c;
419
+ q++;
420
+ while (q < input.length) {
421
+ const ch = input.charCodeAt(q);
422
+ if (ch === 92) {
423
+ q += 2;
424
+ continue;
425
+ }
426
+ if (ch === quote) {
427
+ q++;
428
+ break;
429
+ }
430
+ q++;
431
+ }
432
+ continue;
433
+ }
434
+ if (c === 40 || c === 91 || c === 123)
435
+ depth++;
436
+ else if (c === 41 || c === 93 || c === 125) {
437
+ depth--;
438
+ if (c === 41 && depth === 0)
439
+ break;
440
+ }
441
+ q++;
442
+ }
443
+ if (q >= input.length)
444
+ return false;
445
+ let r = q + 1;
446
+ while (r < input.length && isWsChar(input.charCodeAt(r)))
447
+ r++;
448
+ return input.charCodeAt(r) === 123; // '{'
449
+ }
158
450
  readToken(code) {
451
+ if (this.#componentDepth > 0 && this.#isJsxChildrenContext() && this.#scansChildStatement()) {
452
+ this.#pendingChildStatement = true;
453
+ this.#jsxChildDepthStack.push(this.context.length);
454
+ this.context.push(acorn.tokContexts.b_stat);
455
+ if (isWsChar(code)) {
456
+ this.skipSpace();
457
+ this.start = this.pos;
458
+ if (this.options.locations && typeof this.curPosition === 'function') {
459
+ this.startLoc = this.curPosition();
460
+ }
461
+ }
462
+ return super.readToken(this.input.codePointAt(this.pos));
463
+ }
159
464
  if (this.#componentDepth > 0 && code === 60 && this.#isBlockContext()) {
160
465
  const next = this.input.charCodeAt(this.pos + 1);
161
466
  if (next === 47 || (next >= 65 && next <= 90) || (next >= 97 && next <= 122)) {
@@ -497,7 +802,67 @@ export function VeskParserPlugin(config = {}) {
497
802
  if (prefix.startsWith('<style') && isStyleBoundary(prefix.charCodeAt(6))) {
498
803
  return this.parseStyleElement(startPos, startLoc);
499
804
  }
500
- return super.jsx_parseElementAt(startPos, startLoc);
805
+ return this.#parseElementWithStatements(startPos, startLoc);
806
+ }
807
+ /**
808
+ * Parses an entire JSX element (or fragment) starting after `<`,
809
+ * mirroring the vendored acorn-jsx `jsx_parseElementAt` with an added
810
+ * statement-mode branch: when `#pendingChildStatement` is set (a
811
+ * control-flow header like `if (…) {` appeared where JSX children
812
+ * expect text), a real statement is parsed into the children array.
813
+ */
814
+ #parseElementWithStatements(startPos, startLoc) {
815
+ const node = this.startNodeAt(startPos, startLoc);
816
+ const children = [];
817
+ const openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);
818
+ let closingElement = null;
819
+ if (!openingElement.selfClosing) {
820
+ contents: for (;;) {
821
+ if (this.#pendingChildStatement) {
822
+ this.#pendingChildStatement = false;
823
+ const stmt = this.parseStatement(null);
824
+ children.push(stmt);
825
+ // Restore the JSX-children tokenizer and re-lex the trailing
826
+ // token (lexed under the temporary b_stat context) as a JSX child.
827
+ this.context.length = this.#jsxChildDepthStack.pop() ?? this.context.length;
828
+ this.exprAllowed = true;
829
+ this.pos = this.start;
830
+ this.next();
831
+ continue;
832
+ }
833
+ switch (this.type) {
834
+ case tstt?.jsxTagStart:
835
+ startPos = this.start;
836
+ startLoc = this.startLoc;
837
+ this.next();
838
+ if (this.eat(tt.slash)) {
839
+ closingElement = this.jsx_parseClosingElementAt(startPos, startLoc);
840
+ break contents;
841
+ }
842
+ children.push(this.jsx_parseElementAt(startPos, startLoc));
843
+ break;
844
+ case tstt?.jsxText:
845
+ children.push(this.parseExprAtom());
846
+ break;
847
+ case tt.braceL:
848
+ children.push(this.jsx_parseExpressionContainer());
849
+ break;
850
+ default:
851
+ this.unexpected();
852
+ }
853
+ }
854
+ if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
855
+ this.raise(closingElement.start, "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">");
856
+ }
857
+ }
858
+ const fragmentOrElement = openingElement.name ? "Element" : "Fragment";
859
+ node["opening" + fragmentOrElement] = openingElement;
860
+ node["closing" + fragmentOrElement] = closingElement;
861
+ node.children = children;
862
+ if (this.type === tt.relational && this.value === "<") {
863
+ this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");
864
+ }
865
+ return this.finishNode(node, "JSX" + fragmentOrElement);
501
866
  }
502
867
  jsx_parseOpeningElementAt(startPos, startLoc) {
503
868
  // Manual implementation that treats HTML void elements as
@@ -1 +1 @@
1
- {"version":3,"file":"vsk-imports.d.ts","sourceRoot":"","sources":["../src/vsk-imports.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AAEH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAahE;AA0BD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAUrF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAKnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBhE"}
1
+ {"version":3,"file":"vsk-imports.d.ts","sourceRoot":"","sources":["../src/vsk-imports.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AAEH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAahE;AA0BD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAUrF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAKnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBhE"}
@@ -127,7 +127,9 @@ export function stripTypeImport(importSrc) {
127
127
  return null;
128
128
  const specs = stmt.specifiers || [];
129
129
  const kept = specs.filter((s) => s.importKind !== 'type');
130
- if (kept.length === 0)
130
+ // A side-effect import (`import './x'`) has no specifiers at all — it is NOT
131
+ // type-only and must survive so both bundles run its module-level code.
132
+ if (specs.length > 0 && kept.length === 0)
131
133
  return null;
132
134
  if (kept.length === specs.length)
133
135
  return importSrc;
@@ -1 +1 @@
1
- {"version":3,"file":"vsk-tsx.d.ts","sourceRoot":"","sources":["../src/vsk-tsx.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AAEH,MAAM,WAAW,QAAQ;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,QAAQ,CAAC,EAAE,OAAO,GAAG;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3E,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;IACtG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAk+BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CA0ChG;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAWD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmDrD"}
1
+ {"version":3,"file":"vsk-tsx.d.ts","sourceRoot":"","sources":["../src/vsk-tsx.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AAEH,MAAM,WAAW,QAAQ;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,QAAQ,CAAC,EAAE,OAAO,GAAG;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3E,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;IACtG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AA++BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CA0ChG;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAWD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmDrD"}
package/dist/vsk-tsx.js CHANGED
@@ -569,6 +569,18 @@ function emitJSXChildren(g, source, children, opts) {
569
569
  emitJSXFragment(g, source, child, opts);
570
570
  i++;
571
571
  }
572
+ else if (child.type === 'IfStatement' || child.type === 'ForOfStatement' ||
573
+ child.type === 'ForStatement' || child.type === 'ForInStatement' ||
574
+ child.type === 'WhileStatement' || child.type === 'DoWhileStatement' ||
575
+ child.type === 'SwitchStatement' || child.type === 'TryStatement' ||
576
+ child.type === 'VariableDeclaration' || child.type === 'ExpressionStatement') {
577
+ // Statement-mode control flow nested among JSX children: TSX has no
578
+ // statement children, so wrap each in an IIFE expression container.
579
+ g.addRaw('{(() => { ');
580
+ emitBody(g, source, [child], '', opts);
581
+ g.addRaw(' })()}');
582
+ i++;
583
+ }
572
584
  else {
573
585
  i++;
574
586
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "description": "Vesk compiler — lexer, parser, semantic analysis, IR, codegen",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@sveltejs/acorn-typescript": "^1.0.0",
23
- "@vesk/types": "^0.2.8",
23
+ "@vesk/types": "^0.2.9",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",