@swiftuijs/twill 0.1.1 → 0.2.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.
Files changed (59) hide show
  1. package/README.md +48 -14
  2. package/bin/run.mjs +15 -0
  3. package/bin/twill.mjs +34 -11
  4. package/dist/branches.d.ts +6 -3
  5. package/dist/cli.js +13 -6
  6. package/dist/cli.js.map +1 -1
  7. package/dist/compilation-cache-CuCGz02_.js +172 -0
  8. package/dist/compilation-cache-CuCGz02_.js.map +1 -0
  9. package/dist/compilation-cache.d.ts +21 -0
  10. package/dist/{compiler-Bi9TNOhX.js → compiler-DY1N07np.js} +530 -50
  11. package/dist/compiler-DY1N07np.js.map +1 -0
  12. package/dist/compiler.d.ts +17 -2
  13. package/dist/config.d.ts +1 -6
  14. package/dist/config.js +1 -54
  15. package/dist/configuration-CTJjSu_h.js +139 -0
  16. package/dist/configuration-CTJjSu_h.js.map +1 -0
  17. package/dist/configuration.d.ts +12 -0
  18. package/dist/defer-helper.d.ts +1 -0
  19. package/dist/defer.d.ts +1 -1
  20. package/dist/doctor.js +10 -6
  21. package/dist/doctor.js.map +1 -1
  22. package/dist/editor.d.ts +2 -0
  23. package/dist/editor.js +75 -4
  24. package/dist/editor.js.map +1 -1
  25. package/dist/enums.d.ts +27 -0
  26. package/dist/esbuild.js +1 -1
  27. package/dist/extensions-B1xTkoYP.js +9 -0
  28. package/dist/extensions-B1xTkoYP.js.map +1 -0
  29. package/dist/extensions.d.ts +2 -0
  30. package/dist/{files-D1mERoxc.js → files-BW9geU0_.js} +2 -2
  31. package/dist/files-BW9geU0_.js.map +1 -0
  32. package/dist/index.js +2 -1
  33. package/dist/loader-compiler-DIw9aw5n.js +19 -0
  34. package/dist/loader-compiler-DIw9aw5n.js.map +1 -0
  35. package/dist/loader-compiler.d.ts +4 -0
  36. package/dist/loader.d.ts +4 -0
  37. package/dist/loader.js +29 -22
  38. package/dist/loader.js.map +1 -1
  39. package/dist/parser.d.ts +2 -0
  40. package/dist/{plugin-BGabvyT6.js → plugin-Bks2hxCJ.js} +41 -12
  41. package/dist/plugin-Bks2hxCJ.js.map +1 -0
  42. package/dist/project.js +27 -3
  43. package/dist/project.js.map +1 -1
  44. package/dist/rollup.js +1 -1
  45. package/dist/rspack.js +1 -1
  46. package/dist/syntax.d.ts +2 -0
  47. package/dist/syntax.js +1 -1
  48. package/dist/{transpile-DwVekVFM.js → transpile-DG81yfVs.js} +2 -2
  49. package/dist/{transpile-DwVekVFM.js.map → transpile-DG81yfVs.js.map} +1 -1
  50. package/dist/typescript-plugin.cjs +718 -60
  51. package/dist/typescript-plugin.cjs.map +1 -1
  52. package/dist/vite.js +1 -1
  53. package/dist/webpack.js +1 -1
  54. package/package.json +9 -5
  55. package/schemas/twill.schema.json +6 -0
  56. package/dist/compiler-Bi9TNOhX.js.map +0 -1
  57. package/dist/config.js.map +0 -1
  58. package/dist/files-D1mERoxc.js.map +0 -1
  59. package/dist/plugin-BGabvyT6.js.map +0 -1
@@ -2,7 +2,7 @@ import MagicString from "magic-string";
2
2
  import { Parser, tokTypes } from "acorn";
3
3
  import { tsPlugin } from "acorn-typescript";
4
4
  import jsx from "acorn-jsx";
5
- import { GREATEST_LOWER_BOUND, TraceMap, generatedPositionFor, originalPositionFor } from "@jridgewell/trace-mapping";
5
+ import { GREATEST_LOWER_BOUND, TraceMap, decodedMappings, encodedMappings, generatedPositionFor, originalPositionFor } from "@jridgewell/trace-mapping";
6
6
  //#region src/parser.js
7
7
  var options = {
8
8
  ecmaVersion: "latest",
@@ -80,7 +80,8 @@ function exits(node) {
80
80
  case "BreakStatement":
81
81
  case "ContinueStatement": return true;
82
82
  case "BlockStatement": return node.body.some(exits);
83
- case "IfStatement": return !!node.alternate && exits(node.consequent) && exits(node.alternate);
83
+ case "IfStatement":
84
+ case "TwillIfBinding": return !!node.alternate && exits(node.consequent) && exits(node.alternate);
84
85
  default: return false;
85
86
  }
86
87
  }
@@ -174,6 +175,16 @@ function moveNodes(node, delta, locate) {
174
175
  }
175
176
  }
176
177
  var parsers = /* @__PURE__ */ new Map();
178
+ function associatedEnumAt(input, offset) {
179
+ try {
180
+ const tokens = Parser.tokenizer(input.slice(offset), { ecmaVersion: "latest" });
181
+ tokens.getToken();
182
+ const next = tokens.getToken();
183
+ return next.value === "<" || next.type === tokTypes.braceL && tokens.getToken().type === tokTypes._case && tokens.getToken().type === tokTypes.name;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
177
188
  function parserFor(language) {
178
189
  if (parsers.has(language)) return parsers.get(language);
179
190
  const Base = bases[language];
@@ -183,8 +194,55 @@ function parserFor(language) {
183
194
  lineStarts = null;
184
195
  classSuperDepth = null;
185
196
  subscriptDepth = 0;
197
+ conditionDepth = null;
186
198
  derivedClassElement = false;
187
199
  implicitClosure = null;
200
+ tsParseEnumDeclaration(node, modifiers = {}) {
201
+ if (!associatedEnumAt(this.input, this.end)) return super.tsParseEnumDeclaration(node, modifiers);
202
+ if (modifiers.const || modifiers.declare || this.isAmbientContext) this.raise(node.start, "Associated-value enums cannot be const or ambient declarations.");
203
+ this.expectContextual("enum");
204
+ node.id = this.parseIdent();
205
+ this.checkLValSimple(node.id, 2);
206
+ node.typeParameters = this.tsTryParseTypeParameters(() => {
207
+ if (this.type === tokTypes._in || this.isContextual("out") && this.lookahead().type === tokTypes.name) this.raise(this.start, "Associated-value enum type parameters cannot have const/in/out modifiers.");
208
+ });
209
+ const constant = node.typeParameters?.params.find((parameter) => parameter.const);
210
+ if (constant) this.raise(constant.start, "Associated-value enum type parameters cannot have const/in/out modifiers.");
211
+ node.braceStart = this.start;
212
+ this.expect(tokTypes.braceL);
213
+ node.cases = [];
214
+ const names = /* @__PURE__ */ new Set();
215
+ while (this.type !== tokTypes.braceR) {
216
+ const branch = this.startNode();
217
+ this.expect(tokTypes._case);
218
+ branch.id = this.parseIdent();
219
+ if (names.has(branch.id.name)) this.raise(branch.id.start, "Duplicate enum case.");
220
+ names.add(branch.id.name);
221
+ branch.params = [];
222
+ branch.hasParens = this.eat(tokTypes.parenL);
223
+ if (branch.hasParens) {
224
+ const fields = /* @__PURE__ */ new Set();
225
+ while (this.type !== tokTypes.parenR) {
226
+ const field = this.parseIdent();
227
+ this.checkLValSimple(field);
228
+ if (field.name === "kind") this.raise(field.start, "Enum payload fields cannot use the discriminator name kind.");
229
+ if (fields.has(field.name)) this.raise(field.start, "Duplicate enum payload field.");
230
+ fields.add(field.name);
231
+ if (this.type !== tokTypes.colon) this.raise(this.start, "Enum payload fields require a name and type; optional/default/rest fields are unsupported.");
232
+ field.typeAnnotation = this.tsParseTypeAnnotation();
233
+ branch.params.push(field);
234
+ if (!this.eat(tokTypes.comma)) break;
235
+ }
236
+ this.expect(tokTypes.parenR);
237
+ }
238
+ branch.valueEnd = this.lastTokEnd;
239
+ this.semicolon();
240
+ node.cases.push(this.finishNode(branch, "TwillEnumCase"));
241
+ }
242
+ if (!node.cases.length) this.raise(node.start, "An associated-value enum needs at least one case.");
243
+ this.next();
244
+ return this.finishNode(node, "TwillEnumDeclaration");
245
+ }
188
246
  locate(offset) {
189
247
  if (!this.lineStarts) {
190
248
  this.lineStarts = [0];
@@ -223,7 +281,11 @@ function parserFor(language) {
223
281
  node.cleanup = cleanup;
224
282
  return this.finishNode(node, "DeferStatement");
225
283
  }
226
- if (!this.isContextual("guard") || !guardAt(this.input, this.end, !!this.implicitClosure)) return super.parseStatement(context, ...args);
284
+ if (!this.isContextual("guard") || !guardAt(this.input, this.end, !!this.implicitClosure)) {
285
+ const statement = super.parseStatement(context, ...args);
286
+ if (context && statement.type === "TwillEnumDeclaration") this.raise(statement.start, "An associated-value enum requires a block; add braces around this body.");
287
+ return statement;
288
+ }
227
289
  const node = this.startNode();
228
290
  node.singleStatement = !!context;
229
291
  this.next();
@@ -243,6 +305,36 @@ function parserFor(language) {
243
305
  if (!exits(node.failure)) this.raise(node.failure.start, "Every guard else path must exit with return, throw, break or continue.");
244
306
  return this.finishNode(node, "GuardStatement");
245
307
  }
308
+ parseIfStatement(node) {
309
+ if (/^\s*\(/.test(this.input.slice(this.end))) return super.parseIfStatement(node);
310
+ let binding;
311
+ try {
312
+ binding = Parser.tokenizer(this.input.slice(this.end), options).getToken().type;
313
+ } catch {}
314
+ if (binding !== tokTypes._const) return super.parseIfStatement(node);
315
+ this.next();
316
+ const declaration = this.startNode();
317
+ this.next();
318
+ this.enterScope(0);
319
+ const previous = this.conditionDepth;
320
+ this.conditionDepth = this.subscriptDepth;
321
+ try {
322
+ this.parseVar(declaration, false, "const");
323
+ } finally {
324
+ this.conditionDepth = previous;
325
+ }
326
+ node.binding = this.finishNode(declaration, "VariableDeclaration");
327
+ if (node.binding.declarations.length !== 1) this.raise(node.start, "if const requires one binding declaration.");
328
+ if (this.type !== tokTypes.braceL) this.raise(this.start, "if const requires a braced success branch.");
329
+ node.consequent = this.parseBlock(false);
330
+ this.exitScope();
331
+ node.alternate = null;
332
+ if (this.eat(tokTypes._else)) {
333
+ if (this.type !== tokTypes.braceL && this.type !== tokTypes._if) this.raise(this.start, "if const else requires a block or another if.");
334
+ node.alternate = this.parseStatement("if");
335
+ }
336
+ return this.finishNode(node, "TwillIfBinding");
337
+ }
246
338
  tsParseTypeParameter(...args) {
247
339
  const start = this.start;
248
340
  const loc = this.startLoc;
@@ -314,12 +406,21 @@ function parserFor(language) {
314
406
  }
315
407
  return super.parseExprAtom(...args);
316
408
  }
317
- parseSwitchExpression() {
318
- const node = this.startNode();
319
- this.next();
320
- node.parenStart = this.start;
321
- node.discriminant = this.parseParenExpression();
322
- node.parenEnd = this.lastTokStart;
409
+ parseSwitchExpression(call) {
410
+ const node = call ? this.startNodeAt(call.start, call.loc.start) : this.startNode();
411
+ node.keyword = call ? "match" : "switch";
412
+ if (call) {
413
+ if (call.arguments.length !== 1 || call.arguments[0].type === "SpreadElement") this.raise(call.start, "A match expression requires one subject; parenthesize comma expressions.");
414
+ const opening = Parser.tokenizer(this.input.slice(call.callee.end), options).getToken();
415
+ node.parenStart = call.callee.end + opening.start;
416
+ node.discriminant = call.arguments[0];
417
+ node.parenEnd = call.end - 1;
418
+ } else {
419
+ this.next();
420
+ node.parenStart = this.start;
421
+ node.discriminant = this.parseParenExpression();
422
+ node.parenEnd = this.lastTokStart;
423
+ }
323
424
  this.expect(tokTypes.braceL);
324
425
  node.cases = [];
325
426
  let mode, discriminator, sawDefault = false;
@@ -333,6 +434,34 @@ function parserFor(language) {
333
434
  if (sawDefault) this.raise(branch.start, "Multiple default clauses.");
334
435
  sawDefault = true;
335
436
  branch.test = null;
437
+ } else if (call && this.type !== tokTypes.braceL || !call && this.type.label === "name" && this.value === "enum" && !this.containsEsc) {
438
+ if (mode === "value") this.raise(branch.start, "Cannot mix enum patterns and value cases.");
439
+ if (discriminator !== void 0 && discriminator !== "kind") this.raise(branch.start, "Enum patterns require the kind discriminator.");
440
+ mode = "pattern";
441
+ discriminator = "kind";
442
+ const pattern = this.startNode();
443
+ pattern.explicitKeyword = !call;
444
+ if (!call) this.next();
445
+ let reference = this.parseIdent();
446
+ while (this.eat(tokTypes.dot)) {
447
+ const member = this.startNodeAt(reference.start, reference.loc.start);
448
+ member.object = reference;
449
+ member.property = this.parseIdent(true);
450
+ member.computed = false;
451
+ member.optional = false;
452
+ reference = this.finishNode(member, "MemberExpression");
453
+ }
454
+ if (reference.type !== "MemberExpression") this.raise(reference.start, "An enum pattern requires a qualified case reference.");
455
+ pattern.reference = reference;
456
+ pattern.parenStart = this.start;
457
+ this.expect(tokTypes.parenL);
458
+ if (this.type === tokTypes.braceL) {
459
+ pattern.binding = this.parseObj(true);
460
+ this.checkLValPattern(pattern.binding, 2, false);
461
+ }
462
+ pattern.parenEnd = this.start;
463
+ this.expect(tokTypes.parenR);
464
+ branch.enumPattern = this.finishNode(pattern, "TwillEnumPattern");
336
465
  } else if (this.type === tokTypes.braceL) {
337
466
  if (mode === "value") this.raise(branch.start, "Cannot mix object patterns and value cases.");
338
467
  mode = "pattern";
@@ -378,7 +507,7 @@ function parserFor(language) {
378
507
  node.cases.push(this.finishNode(branch, "TwillSwitchCase"));
379
508
  }
380
509
  this.next();
381
- if (!node.cases.length) this.raise(node.start, "A switch expression requires at least one arm.");
510
+ if (!node.cases.length) this.raise(node.start, `A ${node.keyword} expression requires at least one arm.`);
382
511
  node.discriminator = discriminator;
383
512
  return this.finishNode(node, "TwillSwitchExpression");
384
513
  }
@@ -441,6 +570,13 @@ function parserFor(language) {
441
570
  }
442
571
  parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {
443
572
  const classBody = this.classSuperDepth !== null && this.subscriptDepth === this.classSuperDepth + 1;
573
+ if (!noCalls && !classBody && this.type === tokTypes.braceL && base.type === "CallExpression" && !base.optional && !base.typeParameters && base.callee.type === "Identifier" && base.callee.name === "match" && base.callee.start === base.start && this.input.slice(base.callee.start, base.callee.end) === "match") {
574
+ let first;
575
+ try {
576
+ first = Parser.tokenizer(this.input.slice(this.end), options).getToken().type;
577
+ } catch {}
578
+ if (first === tokTypes._case || first === tokTypes._default) return this.parseSwitchExpression(base);
579
+ }
444
580
  if (!noCalls && !classBody && this.tsMatchLeftRelational?.() && this.start === base.end) {
445
581
  const parameters = this.tsTryParseAndCatch(() => {
446
582
  const parsed = this.tsParseTypeArgumentsInExpression();
@@ -459,6 +595,7 @@ function parserFor(language) {
459
595
  "MemberExpression",
460
596
  "TSInstantiationExpression"
461
597
  ].includes(base.type);
598
+ if (this.conditionDepth !== null && this.subscriptDepth === this.conditionDepth + 1 && this.type === tokTypes.braceL) return base;
462
599
  if (!noCalls && supported && this.type === tokTypes.braceL && !classBody) {
463
600
  if (base.trailing) this.raise(this.start, "Additional trailing closures require labels (for example completion: { ... }).");
464
601
  if (base.type !== "CallExpression" && /[\r\n]/.test(this.input.slice(base.end, this.start))) return base;
@@ -512,15 +649,19 @@ function parse(source, language = "ts", sourceType = "module") {
512
649
  const live = /* @__PURE__ */ new Set();
513
650
  const calls = [];
514
651
  const guards = [];
652
+ const ifBindings = [];
515
653
  const defers = [];
516
654
  const switches = [];
655
+ const enums = [];
517
656
  const visit = (node) => {
518
657
  if (!node || typeof node !== "object") return;
519
658
  if (node.type === "ArrowFunctionExpression" || node.type === "Identifier" && node.start === node.end && node.name === "__twillImplicit") live.add(node);
520
659
  if (node.trailing) calls.push(node);
521
660
  if (node.type === "GuardStatement") guards.push(node);
661
+ if (node.type === "TwillIfBinding") ifBindings.push(node);
522
662
  if (node.type === "DeferStatement") defers.push(node);
523
663
  if (node.type === "TwillSwitchExpression") switches.push(node);
664
+ if (node.type === "TwillEnumDeclaration") enums.push(node);
524
665
  for (const [key, value] of Object.entries(node)) {
525
666
  if (key === "trailing" || key === "loc") continue;
526
667
  if (Array.isArray(value)) value.forEach(visit);
@@ -532,8 +673,10 @@ function parse(source, language = "ts", sourceType = "module") {
532
673
  ast,
533
674
  calls,
534
675
  guards,
676
+ ifBindings,
535
677
  defers,
536
678
  switches,
679
+ enums,
537
680
  comments,
538
681
  closures: parser.closures.filter((closure) => live.has(closure.node)).map((closure) => ({
539
682
  ...closure,
@@ -542,6 +685,11 @@ function parse(source, language = "ts", sourceType = "module") {
542
685
  };
543
686
  }
544
687
  //#endregion
688
+ //#region src/defer-helper.ts
689
+ function syncDeferBody(names) {
690
+ return "\n let " + names.failed + " = false,\n " + names.failure + ";\n while (" + names.stack + "?.length) {\n const " + names.cleanup + " = " + names.stack + ".pop();\n if (" + names.cleanup + ") {\n try {\n " + names.cleanup + "();\n } catch (" + names.error + ") {\n " + names.failed + " = true;\n " + names.failure + " = " + names.error + ";\n }\n }\n }\n if (" + names.failed + ") throw " + names.failure + ";\n";
691
+ }
692
+ //#endregion
545
693
  //#region src/defer.ts
546
694
  var functions = /* @__PURE__ */ new Set([
547
695
  "FunctionDeclaration",
@@ -558,7 +706,7 @@ function children(node, visit) {
558
706
  }
559
707
  }
560
708
  /** Scope-local cleanup callbacks; dynamic registrations use lazy stacks. */
561
- function lowerDefers(source, ast, code, language, usedNames, contentStarts, comments, fail) {
709
+ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comments, runtime, sourceType, fail) {
562
710
  const scopes = [];
563
711
  const visit = (node, scope, parent) => {
564
712
  if (functions.has(node.type) || [
@@ -607,6 +755,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
607
755
  }
608
756
  return start;
609
757
  };
758
+ let sharedHelper;
610
759
  for (const scope of scopes) {
611
760
  if (!scope.defers.length) continue;
612
761
  const { block } = scope;
@@ -631,7 +780,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
631
780
  const declarations = scope.functionBody ? ordinary.filter((node) => node.type === "FunctionDeclaration") : [];
632
781
  if (scope.functionBody && ordinary.some((node) => node.type === "TSDeclareFunction")) fail(ordinary.find((node) => node.type === "TSDeclareFunction"), "Function overloads/ambient function declarations in a defer scope are not supported; move them to a separate scope.");
633
782
  const names = [...new Set(declarations.map((node) => node.id.name))];
634
- code.appendLeft(start, `;${jsdoc}let ${stack}${annotation};${names.length ? `var ${names.join(",")};` : ""}try {\n`);
783
+ code.prependLeft(start, `;${jsdoc}let ${stack}${annotation};${names.length ? `var ${names.join(",")};` : ""}try {\n`);
635
784
  let anchor = start;
636
785
  const initial = /* @__PURE__ */ new Set();
637
786
  for (const statement of ordinary) {
@@ -655,11 +804,37 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
655
804
  }
656
805
  if (single) {
657
806
  const call = allAsync ? `if (${stack}) await ${stack}();` : `${stack}?.();`;
658
- code.prependLeft(block.end - 1, `\n} finally { ${call} }\n`);
807
+ code.appendLeft(block.end - 1, `\n} finally { ${call} }\n`);
808
+ continue;
809
+ }
810
+ if (!anyAsync) {
811
+ let body;
812
+ if (runtime === "external") {
813
+ if (sourceType !== "module") fail(scope.defers[0], "External runtime helpers require module sourceType; use runtime: inline for scripts.");
814
+ sharedHelper ??= fresh("RunDefers");
815
+ body = `${sharedHelper}(${stack});`;
816
+ } else body = syncDeferBody({
817
+ stack,
818
+ failed,
819
+ failure,
820
+ cleanup: callback,
821
+ error
822
+ });
823
+ code.appendLeft(block.end - 1, `} finally { ${body} }`);
659
824
  continue;
660
825
  }
661
826
  const call = mixed ? `if (${callback}.async) await ${callback}.run(); else ${callback}.run();` : `${allAsync ? "await " : ""}${callback}();`;
662
- code.prependLeft(block.end - 1, `} finally { let ${failed} = false, ${failure}; while (${stack}?.length) { const ${callback} = ${stack}.pop(); if (${callback}) { try { ${call} } catch (${error}) { ${failed} = true; ${failure} = ${error}; } } } if (${failed}) throw ${failure}; }`);
827
+ code.appendLeft(block.end - 1, `} finally { let ${failed} = false, ${failure}; while (${stack}?.length) { const ${callback} = ${stack}.pop(); if (${callback}) { try { ${call} } catch (${error}) { ${failed} = true; ${failure} = ${error}; } } } if (${failed}) throw ${failure}; }`);
828
+ }
829
+ if (sharedHelper) {
830
+ let start = ast.body.find((node) => !node.directive).start;
831
+ for (const comment of sortedComments) {
832
+ if (comment.end > start) continue;
833
+ if (/\S/.test(source.slice(comment.end, start))) break;
834
+ if ((source.startsWith("/**", comment.start) || /@ts-(?:ignore|expect-error)\b/.test(comment.value)) && !/@(?:ts-(?:check|nocheck)|jsxImportSource|jsxRuntime)\b/.test(comment.value)) start = comment.start;
835
+ else break;
836
+ }
837
+ code.prependLeft(start, `\nimport { runDefers as ${sharedHelper} } from '@swiftuijs/twill-runtime/helpers/v1';\n`);
663
838
  }
664
839
  }
665
840
  //#endregion
@@ -683,22 +858,68 @@ function suspension(node) {
683
858
  }
684
859
  }
685
860
  }
686
- /** Native switches with per-arm scopes; expression contexts use a synchronous
687
- * lexical IIFE. Direct returns need no extra function or scheduling. */
688
- function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, fail) {
861
+ /** Native switches with per-arm scopes. Direct returns and standalone variable
862
+ * initializers need no extra function; other expressions keep a lexical IIFE. */
863
+ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail) {
689
864
  const returns = /* @__PURE__ */ new Set();
690
- const visit = (node) => {
865
+ const initializers = /* @__PURE__ */ new Map();
866
+ const leadingDoc = (start) => {
867
+ let low = 0, high = comments.length;
868
+ while (low < high) {
869
+ const middle = low + high >>> 1;
870
+ if (comments[middle].end <= start) low = middle + 1;
871
+ else high = middle;
872
+ }
873
+ const comment = comments[low - 1];
874
+ return comment && /^\s*$/.test(source.slice(comment.end, start)) ? comment : void 0;
875
+ };
876
+ const directEval = (node) => {
877
+ if ([
878
+ "FunctionDeclaration",
879
+ "FunctionExpression",
880
+ "ArrowFunctionExpression"
881
+ ].includes(node.type)) return false;
882
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
883
+ return Object.entries(node).some(([key, value]) => ![
884
+ "loc",
885
+ "trailing",
886
+ "tag"
887
+ ].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
888
+ };
889
+ const grouping = (start, end, initializer = false) => {
890
+ const parentheses = [];
891
+ const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
892
+ for (;;) {
893
+ const token = tokens.getToken();
894
+ if (token.type === tokTypes.eof) break;
895
+ if (initializer && token.type === tokTypes.eq) parentheses.length = 0;
896
+ if (token.type === tokTypes.parenL || token.type === tokTypes.parenR) parentheses.push(token);
897
+ }
898
+ for (const token of parentheses) code.remove(start + token.start, start + token.end);
899
+ };
900
+ const visit = (node, parent) => {
901
+ if (node.type === "VariableDeclaration" && node.declarations.length === 1) {
902
+ const declaration = node.declarations[0];
903
+ if (declaration.id.type === "Identifier" && declaration.init?.type === "TwillSwitchExpression" && [
904
+ "Program",
905
+ "BlockStatement",
906
+ "SwitchCase",
907
+ "ExportNamedDeclaration"
908
+ ].includes(parent?.type ?? "")) {
909
+ const statement = parent?.type === "ExportNamedDeclaration" ? parent : node;
910
+ const comment = leadingDoc(statement.start);
911
+ const prefixStart = comment && source.startsWith("/**", comment.start) ? comment.start : statement.start;
912
+ if (!(comment && /@ts-(?:ignore|expect-error|nocheck|check)\b/.test(source.slice(comment.start, comment.end))) && !directEval(declaration.init) && (language.startsWith("ts") || prefixStart === statement.start)) initializers.set(declaration.init, {
913
+ declaration,
914
+ statement,
915
+ prefixStart
916
+ });
917
+ }
918
+ }
691
919
  if (node.type === "ReturnStatement" && node.argument?.type === "TwillSwitchExpression") {
692
920
  returns.add(node.argument);
693
921
  code.remove(node.start, node.start + 6);
694
- for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) {
695
- const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
696
- for (;;) {
697
- const token = tokens.getToken();
698
- if (token.type === tokTypes.eof) break;
699
- if (token.type === tokTypes.parenL || token.type === tokTypes.parenR) code.remove(start + token.start, start + token.end);
700
- }
701
- }
922
+ for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) grouping(start, end);
702
923
  }
703
924
  for (const [key, value] of Object.entries(node)) {
704
925
  if ([
@@ -706,7 +927,7 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
706
927
  "trailing",
707
928
  "tag"
708
929
  ].includes(key)) continue;
709
- for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child);
930
+ for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child, node);
710
931
  }
711
932
  };
712
933
  visit(ast);
@@ -722,34 +943,268 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
722
943
  for (const node of switches) {
723
944
  const direct = returns.has(node);
724
945
  const suspended = !direct && suspension(node);
725
- if (suspended) fail(suspended, "await/yield inside a switch expression requires a direct return. Bind the subject first, or await the whole result explicitly.");
946
+ if (suspended) fail(suspended, `await/yield inside a ${node.keyword} expression requires a direct return. Bind the subject first, or await the whole result explicitly.`);
947
+ const initializer = initializers.get(node);
948
+ const result = initializer ? fresh("Result") : void 0;
949
+ const exit = initializer ? fresh("Exit") : void 0;
726
950
  const subject = fresh("Subject");
727
- code.appendLeft(node.start, `${direct ? "{" : "(() => {"}const ${subject} = `);
951
+ if (initializer) {
952
+ const { declaration, statement, prefixStart } = initializer;
953
+ const annotation = language.startsWith("ts") && declaration.id.typeAnnotation ? `: typeof ${declaration.id.name}` : "";
954
+ grouping(declaration.id.end, node.start, true);
955
+ grouping(node.end, statement.end);
956
+ code.appendLeft(prefixStart, `let ${result}${annotation}; ${exit}: {const ${subject} = `);
957
+ if (prefixStart < statement.start) code.prependRight(prefixStart, "\n");
958
+ code.move(prefixStart, node.start, node.end);
959
+ code.appendRight(node.end, ` ${result}`);
960
+ }
961
+ const valuePrefix = result ? `${result} = (` : "return ";
962
+ const valueSuffix = result ? `); break ${exit};` : ";";
963
+ const kind = node.cases.some((branch) => branch.enumPattern) ? fresh("Kind") : void 0;
964
+ if (!initializer) code.appendLeft(node.start, `${direct ? "{" : "(() => {"}const ${subject} = `);
728
965
  code.remove(node.parenStart, node.parenStart + 1);
729
- code.move(node.start, node.start + 6, node.parenEnd);
730
- code.appendLeft(node.parenEnd, "; ");
731
- code.overwrite(node.parenEnd, node.parenEnd + 1, ` (${subject}${node.discriminator === void 0 ? "" : `[${JSON.stringify(node.discriminator)}]`})`);
732
- for (const branch of node.cases) if (branch.pattern) {
966
+ if (node.keyword === "match") {
967
+ const tokens = Parser.tokenizer(source.slice(node.discriminant.end, node.parenEnd), { ecmaVersion: "latest" });
968
+ for (;;) {
969
+ const token = tokens.getToken();
970
+ if (token.type === tokTypes.eof) break;
971
+ if (token.type === tokTypes.comma) code.remove(node.discriminant.end + token.start, node.discriminant.end + token.end);
972
+ }
973
+ }
974
+ const keywordEnd = node.start + (node.keyword === "match" ? 5 : 6);
975
+ if (node.keyword === "match") code.overwrite(node.start, keywordEnd, "switch");
976
+ code.move(node.start, keywordEnd, node.parenEnd);
977
+ code.appendLeft(node.parenEnd, `; ${kind ? `const ${kind} = ${subject}["kind"]; ` : ""}`);
978
+ code.overwrite(node.parenEnd, node.parenEnd + 1, ` (${kind ?? subject}${kind || node.discriminator === void 0 ? "" : `[${JSON.stringify(node.discriminator)}]`})`);
979
+ for (const branch of node.cases) if (branch.enumPattern) {
980
+ const { reference, binding, start, parenStart, parenEnd, explicitKeyword } = branch.enumPattern;
981
+ const tag = JSON.stringify(reference.property.name);
982
+ if (language.startsWith("ts")) {
983
+ const witness = `(${tag} satisfies (typeof `;
984
+ if (explicitKeyword) code.overwrite(start, start + 4, witness);
985
+ else code.appendLeft(start, witness);
986
+ code.appendLeft(reference.end, ` extends (...args: never[]) => { readonly kind: ${tag} } ? ${tag} : never))`);
987
+ } else if (explicitKeyword) {
988
+ code.overwrite(start, start + 4, tag);
989
+ code.remove(reference.start, reference.end);
990
+ } else code.overwrite(reference.start, reference.end, tag);
991
+ code.remove(parenStart, parenStart + 1);
992
+ code.remove(parenEnd, parenEnd + 1);
993
+ if (binding) {
994
+ code.appendLeft(binding.start, ": { const ");
995
+ code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : valuePrefix}`);
996
+ code.appendLeft(branch.valueEnd, `${branch.throw ? ";" : valueSuffix} }`);
997
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
998
+ } else if (!branch.throw) {
999
+ code.appendLeft(branch.colonStart + 1, ` ${valuePrefix}`);
1000
+ if (result) {
1001
+ code.appendLeft(branch.valueEnd, valueSuffix);
1002
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
1003
+ }
1004
+ }
1005
+ } else if (branch.pattern) {
733
1006
  const { pattern, tag } = branch;
734
1007
  code.appendLeft(tag.value.start, fresh("Tag"));
735
1008
  code.move(tag.value.start, tag.value.end, pattern.start);
736
1009
  code.appendLeft(tag.value.end, ": { const ");
737
- code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : "return "}`);
738
- code.appendLeft(branch.valueEnd, "; }");
1010
+ code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : valuePrefix}`);
1011
+ code.appendLeft(branch.valueEnd, `${branch.throw ? ";" : valueSuffix} }`);
739
1012
  if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
740
- } else if (!branch.throw) code.appendLeft(branch.colonStart + 1, " return ");
741
- const hasDefault = node.cases.some((branch) => !branch.pattern && branch.test === null);
742
- const check = !hasDefault && language.startsWith("ts") ? ` ${subject} satisfies never;` : "";
1013
+ } else if (!branch.throw) {
1014
+ code.appendLeft(branch.colonStart + 1, ` ${valuePrefix}`);
1015
+ if (result) {
1016
+ code.appendLeft(branch.valueEnd, valueSuffix);
1017
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
1018
+ }
1019
+ }
1020
+ const hasDefault = node.cases.some((branch) => !branch.pattern && !branch.enumPattern && branch.test === null);
1021
+ const check = !hasDefault && language.startsWith("ts") ? ` ${kind ? `(0 as unknown as typeof ${kind})` : subject} satisfies never;` : "";
743
1022
  const failure = hasDefault ? "" : `${check} throw new TypeError('Non-exhaustive switch expression');`;
744
- code.overwrite(node.end - 1, node.end, `}${failure}${direct ? "}" : "})()"}`);
1023
+ code.overwrite(node.end - 1, node.end, `}${failure}${direct || initializer ? "}" : "})()"}`, { contentOnly: true });
745
1024
  }
746
1025
  }
747
1026
  //#endregion
748
- //#region src/compiler.ts
749
- var extensions = [".twill", ".twillx"];
750
- function isTwillFile(id) {
751
- return extensions.some((extension) => id.split(/[?#]/, 1)[0].endsWith(extension));
1027
+ //#region src/enums.ts
1028
+ /** Find free type references, respecting generic function/mapped/infer binders. */
1029
+ function references(node, names, found = /* @__PURE__ */ new Set(), bound = /* @__PURE__ */ new Set()) {
1030
+ const local = new Set(bound);
1031
+ for (const parameter of node.typeParameters?.params ?? []) local.add(parameter.name);
1032
+ if (node.type === "TSMappedType") local.add(node.typeParameter.name);
1033
+ if (node.type === "TSInferType") local.add(node.typeParameter.name);
1034
+ if (node.type === "TSTypeReference" && node.typeName.type === "Identifier") {
1035
+ const name = node.typeName.name;
1036
+ if (names.has(name) && !local.has(name)) found.add(name);
1037
+ }
1038
+ for (const [key, value] of Object.entries(node)) {
1039
+ if (["loc", "typeName"].includes(key)) continue;
1040
+ for (const child of Array.isArray(value) ? value : [value]) {
1041
+ if (!child?.type) continue;
1042
+ const childBound = new Set(local);
1043
+ if (node.type === "TSConditionalType" && key === "trueType") {
1044
+ const bind = (part) => {
1045
+ if (part.type === "TSInferType") childBound.add(part.typeParameter.name);
1046
+ for (const [property, nested] of Object.entries(part)) {
1047
+ if (property === "loc") continue;
1048
+ if (part.type === "TSConditionalType" && property === "extendsType") continue;
1049
+ for (const item of Array.isArray(nested) ? nested : [nested]) if (item?.type) bind(item);
1050
+ }
1051
+ };
1052
+ bind(node.extendsType);
1053
+ }
1054
+ references(child, names, found, childBound);
1055
+ }
1056
+ }
1057
+ return found;
1058
+ }
1059
+ function enumTypeParameters(node, branch) {
1060
+ const parameters = node.typeParameters?.params ?? [];
1061
+ const names = new Set(parameters.map((parameter) => parameter.name));
1062
+ const used = /* @__PURE__ */ new Set();
1063
+ for (const field of branch.params) references(field.typeAnnotation, names, used);
1064
+ for (let size = -1; size !== used.size;) {
1065
+ size = used.size;
1066
+ for (const parameter of parameters) {
1067
+ if (!used.has(parameter.name)) continue;
1068
+ if (parameter.constraint) references(parameter.constraint, names, used);
1069
+ if (parameter.default) references(parameter.default, names, used);
1070
+ }
1071
+ }
1072
+ return parameters.filter((parameter) => used.has(parameter.name));
752
1073
  }
1074
+ /** Tagged records and precise factories; no application runtime is introduced. */
1075
+ function lowerEnums(source, ast, enums, code) {
1076
+ const insertions = [];
1077
+ const exported = /* @__PURE__ */ new Set();
1078
+ const visit = (node) => {
1079
+ if (node.type === "ExportNamedDeclaration") exported.add(node.declaration);
1080
+ for (const [key, value] of Object.entries(node)) {
1081
+ if (["loc", "trailing"].includes(key)) continue;
1082
+ for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child);
1083
+ }
1084
+ };
1085
+ visit(ast);
1086
+ for (const node of enums) {
1087
+ const replacement = {
1088
+ anchor: node.braceStart,
1089
+ before: false,
1090
+ text: "= ",
1091
+ copies: []
1092
+ };
1093
+ for (const [index, branch] of node.cases.entries()) {
1094
+ if (index) replacement.text += " | ";
1095
+ replacement.text += `{ readonly kind: ${JSON.stringify(branch.id.name)}; `;
1096
+ for (const field of branch.params) {
1097
+ replacement.text += "readonly ";
1098
+ const text = source.slice(field.start, field.typeAnnotation.end);
1099
+ replacement.copies.push({
1100
+ start: replacement.text.length,
1101
+ sourceStart: field.start,
1102
+ length: text.length
1103
+ });
1104
+ replacement.text += text + "; ";
1105
+ }
1106
+ replacement.text += "}";
1107
+ }
1108
+ replacement.text += `; ${exported.has(node) ? "export " : ""}const `;
1109
+ replacement.copies.push({
1110
+ start: replacement.text.length,
1111
+ sourceStart: node.id.start,
1112
+ length: node.id.end - node.id.start
1113
+ });
1114
+ replacement.text += source.slice(node.id.start, node.id.end) + " = {";
1115
+ insertions.push(replacement);
1116
+ code.overwrite(node.start, node.start + 4, "type");
1117
+ code.overwrite(node.braceStart, node.braceStart + 1, replacement.text);
1118
+ for (const branch of node.cases) {
1119
+ code.remove(branch.start, branch.start + 4);
1120
+ const parameters = enumTypeParameters(node, branch);
1121
+ const copied = {
1122
+ anchor: branch.id.end,
1123
+ before: true,
1124
+ text: parameters.length ? "<" : "",
1125
+ copies: []
1126
+ };
1127
+ for (const [index, parameter] of parameters.entries()) {
1128
+ if (index) copied.text += ", ";
1129
+ const text = source.slice(parameter.start, parameter.end);
1130
+ copied.copies.push({
1131
+ start: copied.text.length,
1132
+ sourceStart: parameter.start,
1133
+ length: text.length
1134
+ });
1135
+ copied.text += text;
1136
+ }
1137
+ if (parameters.length) copied.text += ">";
1138
+ const generic = copied.text;
1139
+ if (generic) insertions.push(copied);
1140
+ code.appendLeft(branch.id.end, generic + (branch.hasParens ? "" : "()"));
1141
+ const fields = branch.params.map((field) => field.name);
1142
+ code.appendLeft(branch.valueEnd, `{ return { kind: ${JSON.stringify(branch.id.name)}${fields.length ? ", " + fields.join(", ") : ""} } as const; },`);
1143
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
1144
+ }
1145
+ code.appendLeft(node.end, ";");
1146
+ }
1147
+ return insertions;
1148
+ }
1149
+ /** Generated type copies map to their actual source tokens, including .d.ts maps. */
1150
+ function mapEnumCopies(source, generated, map, insertions) {
1151
+ const starts = (text) => {
1152
+ const result = [0];
1153
+ for (let index = 0; index < text.length; index++) if (text[index] === "\n") result.push(index + 1);
1154
+ return result;
1155
+ };
1156
+ const originalLines = starts(source), generatedLines = starts(generated);
1157
+ const position = (lines, offset) => {
1158
+ let line = 0;
1159
+ while (line + 1 < lines.length && lines[line + 1] <= offset) line++;
1160
+ return {
1161
+ line,
1162
+ column: offset - lines[line]
1163
+ };
1164
+ };
1165
+ const trace = new TraceMap(map);
1166
+ const decoded = decodedMappings(trace);
1167
+ const copies = [];
1168
+ for (const insertion of insertions) {
1169
+ const anchor = position(originalLines, insertion.anchor);
1170
+ const location = generatedPositionFor(trace, {
1171
+ source: map.sources[0],
1172
+ line: anchor.line + 1,
1173
+ column: anchor.column
1174
+ });
1175
+ const offset = generatedLines[location.line - 1] + location.column - (insertion.before ? insertion.text.length : 0);
1176
+ if (generated.slice(offset, offset + insertion.text.length) !== insertion.text) throw new Error("Enum mapping anchor does not match generated text.");
1177
+ for (const copy of insertion.copies) {
1178
+ copies.push({
1179
+ generatedStart: offset + copy.start,
1180
+ sourceStart: copy.sourceStart,
1181
+ length: copy.length
1182
+ });
1183
+ for (let index = 0; index < copy.length; index++) {
1184
+ const target = position(generatedLines, offset + copy.start + index);
1185
+ const original = position(originalLines, copy.sourceStart + index);
1186
+ const line = decoded[target.line];
1187
+ const entry = [
1188
+ target.column,
1189
+ 0,
1190
+ original.line,
1191
+ original.column
1192
+ ];
1193
+ const existing = line.findIndex((segment) => segment[0] === target.column);
1194
+ if (existing >= 0) line[existing] = entry;
1195
+ else line.push(entry);
1196
+ }
1197
+ }
1198
+ }
1199
+ for (const line of decoded) line.sort((left, right) => left[0] - right[0]);
1200
+ map.mappings = encodedMappings(new TraceMap({
1201
+ ...JSON.parse(map.toString()),
1202
+ mappings: decoded
1203
+ }));
1204
+ return copies;
1205
+ }
1206
+ //#endregion
1207
+ //#region src/compiler.ts
753
1208
  function inferLanguage(filename) {
754
1209
  if (/\.(?:twillx|tsx)$/.test(filename)) return "tsx";
755
1210
  if (/\.jsx$/.test(filename)) return "jsx";
@@ -792,6 +1247,7 @@ function calleeName(node) {
792
1247
  }
793
1248
  }
794
1249
  function transform(source, options = {}) {
1250
+ if (options.runtime !== void 0 && !["inline", "external"].includes(options.runtime)) throw new TypeError("runtime must be inline or external");
795
1251
  const filename = options.filename ?? "input.twill";
796
1252
  let parsed;
797
1253
  try {
@@ -800,11 +1256,28 @@ function transform(source, options = {}) {
800
1256
  throw new TwillSyntaxError(source, filename, error);
801
1257
  }
802
1258
  const code = new MagicString(source);
1259
+ const enumInsertions = parsed.enums.length ? lowerEnums(source, parsed.ast, parsed.enums, code) : [];
803
1260
  const usedNames = /* @__PURE__ */ new Set();
804
- if (parsed.guards.filter((guard) => guard.binding && guard.binding.declarations[0].id.type !== "Identifier").length || parsed.switches.length || parsed.defers.length || parsed.closures.some((closure) => closure.implicitMembers.length) || /\.twillx$/.test(filename.split(/[?#]/, 1)[0])) walk(parsed.ast, (node) => {
805
- if (node.type === "Identifier") usedNames.add(node.name);
1261
+ if (parsed.guards.filter((guard) => guard.binding && guard.binding.declarations[0].id.type !== "Identifier").length || parsed.ifBindings.length || parsed.switches.length || parsed.defers.length || parsed.closures.some((closure) => closure.implicitMembers.length) || /\.twillx$/.test(filename.split(/[?#]/, 1)[0])) walk(parsed.ast, (node) => {
1262
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
806
1263
  });
807
1264
  let guardCounter = 0;
1265
+ const ifBindingStarts = new Set(parsed.ifBindings.map((node) => node.consequent.start + 1));
1266
+ for (const statement of parsed.ifBindings) {
1267
+ const binding = statement.binding.declarations[0].id;
1268
+ let temporary;
1269
+ do
1270
+ temporary = `__twillIf${guardCounter++}`;
1271
+ while (usedNames.has(temporary));
1272
+ usedNames.add(temporary);
1273
+ const patternEnd = binding.typeAnnotation?.start ?? binding.end;
1274
+ code.overwrite(statement.start, binding.start, `{const ${temporary}`);
1275
+ code.move(binding.start, patternEnd, statement.consequent.start + 1);
1276
+ code.prependRight(binding.start, "const ");
1277
+ code.appendLeft(patternEnd, ` = ${temporary};`);
1278
+ code.prependLeft(statement.consequent.start, `; if (${temporary} !== null && ${temporary} !== void 0) `);
1279
+ code.appendLeft(statement.end, "}");
1280
+ }
808
1281
  for (const guard of parsed.guards) {
809
1282
  if (guard.singleStatement) {
810
1283
  code.prependLeft(guard.start, "{");
@@ -1011,13 +1484,15 @@ function transform(source, options = {}) {
1011
1484
  const collect = (statement) => {
1012
1485
  switch (statement.type) {
1013
1486
  case "ExpressionStatement":
1014
- code.prependLeft(statement.start, `${collector}.push(`);
1487
+ if (ifBindingStarts.has(statement.start)) code.appendRight(statement.start, `${collector}.push(`);
1488
+ else code.prependLeft(statement.start, `${collector}.push(`);
1015
1489
  code.appendLeft(source[statement.end - 1] === ";" ? statement.end - 1 : statement.end, ")");
1016
1490
  break;
1017
1491
  case "BlockStatement":
1018
1492
  statement.body.forEach(collect);
1019
1493
  break;
1020
1494
  case "IfStatement":
1495
+ case "TwillIfBinding":
1021
1496
  collect(statement.consequent);
1022
1497
  if (statement.alternate) collect(statement.alternate);
1023
1498
  break;
@@ -1061,7 +1536,7 @@ function transform(source, options = {}) {
1061
1536
  if (component) code.prependLeft(closures.at(-1).end, (vue ? "})}" : directChild || metadataByNode.get(closures[0]).header ? ")}" : ")()}") + `</${calleeName(node.callee)}>)`);
1062
1537
  else code.appendLeft(closures.at(-1).end, ")");
1063
1538
  }
1064
- if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, (node, message) => {
1539
+ if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, parsed.comments, (node, message) => {
1065
1540
  throw new TwillSyntaxError(source, filename, {
1066
1541
  message,
1067
1542
  pos: node.start,
@@ -1072,7 +1547,7 @@ function transform(source, options = {}) {
1072
1547
  if (parsed.defers.length) {
1073
1548
  const starts = /* @__PURE__ */ new Map();
1074
1549
  for (const metadata of parsed.closures) if (metadata.header) starts.set(metadata.node.body, metadata.header.end);
1075
- lowerDefers(source, parsed.ast, code, options.language ?? inferLanguage(filename), usedNames, starts, parsed.comments, (node, message) => {
1550
+ lowerDefers(source, parsed.ast, code, options.language ?? inferLanguage(filename), usedNames, starts, parsed.comments, options.runtime ?? "inline", options.sourceType ?? "module", (node, message) => {
1076
1551
  throw new TwillSyntaxError(source, filename, {
1077
1552
  message,
1078
1553
  pos: node.start,
@@ -1086,14 +1561,19 @@ function transform(source, options = {}) {
1086
1561
  includeContent: true,
1087
1562
  hires: true
1088
1563
  });
1564
+ const enumCopies = enumInsertions.length ? mapEnumCopies(source, code.toString(), map, enumInsertions) : [];
1089
1565
  return {
1090
1566
  code: code.toString(),
1091
1567
  map,
1092
1568
  changed: code.hasChanged(),
1093
1569
  closures: parsed.closures.length,
1094
1570
  guards: parsed.guards.length,
1571
+ ifBindings: parsed.ifBindings.length,
1095
1572
  defers: parsed.defers.length,
1096
1573
  switches: parsed.switches.length,
1574
+ associatedEnums: parsed.enums,
1575
+ enumPatterns: parsed.switches.flatMap((node) => node.cases.flatMap((branch) => branch.enumPattern ? [branch.enumPattern] : [])),
1576
+ enumCopies,
1097
1577
  componentProps
1098
1578
  };
1099
1579
  }
@@ -1122,6 +1602,6 @@ function generatedPosition(result, line, column) {
1122
1602
  });
1123
1603
  }
1124
1604
  //#endregion
1125
- export { isTwillFile as a, parse as c, inferLanguage as i, extensions as n, originalPosition as o, generatedPosition as r, transform as s, TwillSyntaxError as t };
1605
+ export { transform as a, originalPosition as i, generatedPosition as n, parse as o, inferLanguage as r, TwillSyntaxError as t };
1126
1606
 
1127
- //# sourceMappingURL=compiler-Bi9TNOhX.js.map
1607
+ //# sourceMappingURL=compiler-DY1N07np.js.map