@swiftuijs/twill 0.1.2 → 0.3.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 (60) hide show
  1. package/README.md +57 -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-7SugqJ4n.js +172 -0
  8. package/dist/compilation-cache-7SugqJ4n.js.map +1 -0
  9. package/dist/compilation-cache.d.ts +21 -0
  10. package/dist/{compiler-Bi9TNOhX.js → compiler-CbNSBUI2.js} +583 -64
  11. package/dist/compiler-CbNSBUI2.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-BVMf6Q_j.js +149 -0
  16. package/dist/configuration-BVMf6Q_j.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 +2 -2
  20. package/dist/doctor.js +9 -5
  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-D1teTrXw.js +19 -0
  34. package/dist/loader-compiler-D1teTrXw.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-BHfiQ2L-.js} +41 -12
  41. package/dist/plugin-BHfiQ2L-.js.map +1 -0
  42. package/dist/project.js +39 -11
  43. package/dist/project.js.map +1 -1
  44. package/dist/recovery.d.ts +2 -2
  45. package/dist/rollup.js +1 -1
  46. package/dist/rspack.js +1 -1
  47. package/dist/syntax.d.ts +2 -0
  48. package/dist/syntax.js +1 -1
  49. package/dist/{transpile-DwVekVFM.js → transpile-D8P8Lsbx.js} +2 -2
  50. package/dist/{transpile-DwVekVFM.js.map → transpile-D8P8Lsbx.js.map} +1 -1
  51. package/dist/typescript-plugin.cjs +783 -82
  52. package/dist/typescript-plugin.cjs.map +1 -1
  53. package/dist/vite.js +1 -1
  54. package/dist/webpack.js +1 -1
  55. package/package.json +8 -4
  56. package/schemas/twill.schema.json +6 -0
  57. package/dist/compiler-Bi9TNOhX.js.map +0 -1
  58. package/dist/config.js.map +0 -1
  59. package/dist/files-D1mERoxc.js.map +0 -1
  60. package/dist/plugin-BGabvyT6.js.map +0 -1
@@ -38,6 +38,7 @@ let node_fs = require("node:fs");
38
38
  let node_path = require("node:path");
39
39
  let typescript = require("typescript");
40
40
  typescript = __toESM(typescript, 1);
41
+ let node_crypto = require("node:crypto");
41
42
  //#region ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
42
43
  var comma = ",".charCodeAt(0);
43
44
  var semicolon = ";".charCodeAt(0);
@@ -15049,7 +15050,8 @@ function exits(node) {
15049
15050
  case "BreakStatement":
15050
15051
  case "ContinueStatement": return true;
15051
15052
  case "BlockStatement": return node.body.some(exits);
15052
- case "IfStatement": return !!node.alternate && exits(node.consequent) && exits(node.alternate);
15053
+ case "IfStatement":
15054
+ case "TwillIfBinding": return !!node.alternate && exits(node.consequent) && exits(node.alternate);
15053
15055
  default: return false;
15054
15056
  }
15055
15057
  }
@@ -15143,17 +15145,83 @@ function moveNodes(node, delta, locate) {
15143
15145
  }
15144
15146
  }
15145
15147
  var parsers = /* @__PURE__ */ new Map();
15148
+ function associatedEnumAt(input, offset) {
15149
+ try {
15150
+ const tokens = Parser.tokenizer(input.slice(offset), { ecmaVersion: "latest" });
15151
+ tokens.getToken();
15152
+ const next = tokens.getToken();
15153
+ return next.value === "<" || next.type === types$1.braceL && tokens.getToken().type === types$1._case && tokens.getToken().type === types$1.name;
15154
+ } catch {
15155
+ return false;
15156
+ }
15157
+ }
15146
15158
  function parserFor(language) {
15147
15159
  if (parsers.has(language)) return parsers.get(language);
15148
15160
  const Base = bases[language];
15149
15161
  class TwillParser extends Base {
15162
+ declareName(name, bindingType, position) {
15163
+ if (bindingType === 6 || bindingType === 7) {
15164
+ const scope = this.currentScope();
15165
+ (scope.types ??= []).push(name);
15166
+ if (this.inModule && scope.flags & 1) delete this.undefinedExports[name];
15167
+ return;
15168
+ }
15169
+ super.declareName(name, bindingType, position);
15170
+ }
15150
15171
  skipHeader = null;
15151
15172
  closures = [];
15152
15173
  lineStarts = null;
15153
15174
  classSuperDepth = null;
15154
15175
  subscriptDepth = 0;
15176
+ conditionDepth = null;
15155
15177
  derivedClassElement = false;
15156
15178
  implicitClosure = null;
15179
+ tsParseEnumDeclaration(node, modifiers = {}) {
15180
+ if (!associatedEnumAt(this.input, this.end)) return super.tsParseEnumDeclaration(node, modifiers);
15181
+ if (modifiers.const || modifiers.declare || this.isAmbientContext) this.raise(node.start, "Associated-value enums cannot be const or ambient declarations.");
15182
+ this.expectContextual("enum");
15183
+ node.id = this.parseIdent();
15184
+ this.checkLValSimple(node.id, 2);
15185
+ node.typeParameters = this.tsTryParseTypeParameters(() => {
15186
+ if (this.type === types$1._in || this.isContextual("out") && this.lookahead().type === types$1.name) this.raise(this.start, "Associated-value enum type parameters cannot have const/in/out modifiers.");
15187
+ });
15188
+ const constant = node.typeParameters?.params.find((parameter) => parameter.const);
15189
+ if (constant) this.raise(constant.start, "Associated-value enum type parameters cannot have const/in/out modifiers.");
15190
+ node.braceStart = this.start;
15191
+ this.expect(types$1.braceL);
15192
+ node.cases = [];
15193
+ const names = /* @__PURE__ */ new Set();
15194
+ while (this.type !== types$1.braceR) {
15195
+ const branch = this.startNode();
15196
+ this.expect(types$1._case);
15197
+ branch.id = this.parseIdent();
15198
+ if (names.has(branch.id.name)) this.raise(branch.id.start, "Duplicate enum case.");
15199
+ names.add(branch.id.name);
15200
+ branch.params = [];
15201
+ branch.hasParens = this.eat(types$1.parenL);
15202
+ if (branch.hasParens) {
15203
+ const fields = /* @__PURE__ */ new Set();
15204
+ while (this.type !== types$1.parenR) {
15205
+ const field = this.parseIdent();
15206
+ this.checkLValSimple(field);
15207
+ if (field.name === "kind") this.raise(field.start, "Enum payload fields cannot use the discriminator name kind.");
15208
+ if (fields.has(field.name)) this.raise(field.start, "Duplicate enum payload field.");
15209
+ fields.add(field.name);
15210
+ if (this.type !== types$1.colon) this.raise(this.start, "Enum payload fields require a name and type; optional/default/rest fields are unsupported.");
15211
+ field.typeAnnotation = this.tsParseTypeAnnotation();
15212
+ branch.params.push(field);
15213
+ if (!this.eat(types$1.comma)) break;
15214
+ }
15215
+ this.expect(types$1.parenR);
15216
+ }
15217
+ branch.valueEnd = this.lastTokEnd;
15218
+ this.semicolon();
15219
+ node.cases.push(this.finishNode(branch, "TwillEnumCase"));
15220
+ }
15221
+ if (!node.cases.length) this.raise(node.start, "An associated-value enum needs at least one case.");
15222
+ this.next();
15223
+ return this.finishNode(node, "TwillEnumDeclaration");
15224
+ }
15157
15225
  locate(offset) {
15158
15226
  if (!this.lineStarts) {
15159
15227
  this.lineStarts = [0];
@@ -15192,7 +15260,11 @@ function parserFor(language) {
15192
15260
  node.cleanup = cleanup;
15193
15261
  return this.finishNode(node, "DeferStatement");
15194
15262
  }
15195
- if (!this.isContextual("guard") || !guardAt(this.input, this.end, !!this.implicitClosure)) return super.parseStatement(context, ...args);
15263
+ if (!this.isContextual("guard") || !guardAt(this.input, this.end, !!this.implicitClosure)) {
15264
+ const statement = super.parseStatement(context, ...args);
15265
+ if (context && statement.type === "TwillEnumDeclaration") this.raise(statement.start, "An associated-value enum requires a block; add braces around this body.");
15266
+ return statement;
15267
+ }
15196
15268
  const node = this.startNode();
15197
15269
  node.singleStatement = !!context;
15198
15270
  this.next();
@@ -15212,6 +15284,36 @@ function parserFor(language) {
15212
15284
  if (!exits(node.failure)) this.raise(node.failure.start, "Every guard else path must exit with return, throw, break or continue.");
15213
15285
  return this.finishNode(node, "GuardStatement");
15214
15286
  }
15287
+ parseIfStatement(node) {
15288
+ if (/^\s*\(/.test(this.input.slice(this.end))) return super.parseIfStatement(node);
15289
+ let binding;
15290
+ try {
15291
+ binding = Parser.tokenizer(this.input.slice(this.end), options).getToken().type;
15292
+ } catch {}
15293
+ if (binding !== types$1._const) return super.parseIfStatement(node);
15294
+ this.next();
15295
+ const declaration = this.startNode();
15296
+ this.next();
15297
+ this.enterScope(0);
15298
+ const previous = this.conditionDepth;
15299
+ this.conditionDepth = this.subscriptDepth;
15300
+ try {
15301
+ this.parseVar(declaration, false, "const");
15302
+ } finally {
15303
+ this.conditionDepth = previous;
15304
+ }
15305
+ node.binding = this.finishNode(declaration, "VariableDeclaration");
15306
+ if (node.binding.declarations.length !== 1) this.raise(node.start, "if const requires one binding declaration.");
15307
+ if (this.type !== types$1.braceL) this.raise(this.start, "if const requires a braced success branch.");
15308
+ node.consequent = this.parseBlock(false);
15309
+ this.exitScope();
15310
+ node.alternate = null;
15311
+ if (this.eat(types$1._else)) {
15312
+ if (this.type !== types$1.braceL && this.type !== types$1._if) this.raise(this.start, "if const else requires a block or another if.");
15313
+ node.alternate = this.parseStatement("if");
15314
+ }
15315
+ return this.finishNode(node, "TwillIfBinding");
15316
+ }
15215
15317
  tsParseTypeParameter(...args) {
15216
15318
  const start = this.start;
15217
15319
  const loc = this.startLoc;
@@ -15283,12 +15385,21 @@ function parserFor(language) {
15283
15385
  }
15284
15386
  return super.parseExprAtom(...args);
15285
15387
  }
15286
- parseSwitchExpression() {
15287
- const node = this.startNode();
15288
- this.next();
15289
- node.parenStart = this.start;
15290
- node.discriminant = this.parseParenExpression();
15291
- node.parenEnd = this.lastTokStart;
15388
+ parseSwitchExpression(call) {
15389
+ const node = call ? this.startNodeAt(call.start, call.loc.start) : this.startNode();
15390
+ node.keyword = call ? "match" : "switch";
15391
+ if (call) {
15392
+ if (call.arguments.length !== 1 || call.arguments[0].type === "SpreadElement") this.raise(call.start, "A match expression requires one subject; parenthesize comma expressions.");
15393
+ const opening = Parser.tokenizer(this.input.slice(call.callee.end), options).getToken();
15394
+ node.parenStart = call.callee.end + opening.start;
15395
+ node.discriminant = call.arguments[0];
15396
+ node.parenEnd = call.end - 1;
15397
+ } else {
15398
+ this.next();
15399
+ node.parenStart = this.start;
15400
+ node.discriminant = this.parseParenExpression();
15401
+ node.parenEnd = this.lastTokStart;
15402
+ }
15292
15403
  this.expect(types$1.braceL);
15293
15404
  node.cases = [];
15294
15405
  let mode, discriminator, sawDefault = false;
@@ -15302,6 +15413,34 @@ function parserFor(language) {
15302
15413
  if (sawDefault) this.raise(branch.start, "Multiple default clauses.");
15303
15414
  sawDefault = true;
15304
15415
  branch.test = null;
15416
+ } else if (call && this.type !== types$1.braceL || !call && this.type.label === "name" && this.value === "enum" && !this.containsEsc) {
15417
+ if (mode === "value") this.raise(branch.start, "Cannot mix enum patterns and value cases.");
15418
+ if (discriminator !== void 0 && discriminator !== "kind") this.raise(branch.start, "Enum patterns require the kind discriminator.");
15419
+ mode = "pattern";
15420
+ discriminator = "kind";
15421
+ const pattern = this.startNode();
15422
+ pattern.explicitKeyword = !call;
15423
+ if (!call) this.next();
15424
+ let reference = this.parseIdent();
15425
+ while (this.eat(types$1.dot)) {
15426
+ const member = this.startNodeAt(reference.start, reference.loc.start);
15427
+ member.object = reference;
15428
+ member.property = this.parseIdent(true);
15429
+ member.computed = false;
15430
+ member.optional = false;
15431
+ reference = this.finishNode(member, "MemberExpression");
15432
+ }
15433
+ if (reference.type !== "MemberExpression") this.raise(reference.start, "An enum pattern requires a qualified case reference.");
15434
+ pattern.reference = reference;
15435
+ pattern.parenStart = this.start;
15436
+ this.expect(types$1.parenL);
15437
+ if (this.type === types$1.braceL) {
15438
+ pattern.binding = this.parseObj(true);
15439
+ this.checkLValPattern(pattern.binding, 2, false);
15440
+ }
15441
+ pattern.parenEnd = this.start;
15442
+ this.expect(types$1.parenR);
15443
+ branch.enumPattern = this.finishNode(pattern, "TwillEnumPattern");
15305
15444
  } else if (this.type === types$1.braceL) {
15306
15445
  if (mode === "value") this.raise(branch.start, "Cannot mix object patterns and value cases.");
15307
15446
  mode = "pattern";
@@ -15347,7 +15486,7 @@ function parserFor(language) {
15347
15486
  node.cases.push(this.finishNode(branch, "TwillSwitchCase"));
15348
15487
  }
15349
15488
  this.next();
15350
- if (!node.cases.length) this.raise(node.start, "A switch expression requires at least one arm.");
15489
+ if (!node.cases.length) this.raise(node.start, `A ${node.keyword} expression requires at least one arm.`);
15351
15490
  node.discriminator = discriminator;
15352
15491
  return this.finishNode(node, "TwillSwitchExpression");
15353
15492
  }
@@ -15410,6 +15549,13 @@ function parserFor(language) {
15410
15549
  }
15411
15550
  parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {
15412
15551
  const classBody = this.classSuperDepth !== null && this.subscriptDepth === this.classSuperDepth + 1;
15552
+ if (!noCalls && !classBody && this.type === types$1.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") {
15553
+ let first;
15554
+ try {
15555
+ first = Parser.tokenizer(this.input.slice(this.end), options).getToken().type;
15556
+ } catch {}
15557
+ if (first === types$1._case || first === types$1._default) return this.parseSwitchExpression(base);
15558
+ }
15413
15559
  if (!noCalls && !classBody && this.tsMatchLeftRelational?.() && this.start === base.end) {
15414
15560
  const parameters = this.tsTryParseAndCatch(() => {
15415
15561
  const parsed = this.tsParseTypeArgumentsInExpression();
@@ -15428,6 +15574,7 @@ function parserFor(language) {
15428
15574
  "MemberExpression",
15429
15575
  "TSInstantiationExpression"
15430
15576
  ].includes(base.type);
15577
+ if (this.conditionDepth !== null && this.subscriptDepth === this.conditionDepth + 1 && this.type === types$1.braceL) return base;
15431
15578
  if (!noCalls && supported && this.type === types$1.braceL && !classBody) {
15432
15579
  if (base.trailing) this.raise(this.start, "Additional trailing closures require labels (for example completion: { ... }).");
15433
15580
  if (base.type !== "CallExpression" && /[\r\n]/.test(this.input.slice(base.end, this.start))) return base;
@@ -15481,18 +15628,23 @@ function parse$1(source, language = "ts", sourceType = "module") {
15481
15628
  const live = /* @__PURE__ */ new Set();
15482
15629
  const calls = [];
15483
15630
  const guards = [];
15631
+ const ifBindings = [];
15484
15632
  const defers = [];
15485
15633
  const switches = [];
15634
+ const enums = [];
15486
15635
  const visit = (node) => {
15487
15636
  if (!node || typeof node !== "object") return;
15488
15637
  if (node.type === "ArrowFunctionExpression" || node.type === "Identifier" && node.start === node.end && node.name === "__twillImplicit") live.add(node);
15489
15638
  if (node.trailing) calls.push(node);
15490
15639
  if (node.type === "GuardStatement") guards.push(node);
15640
+ if (node.type === "TwillIfBinding") ifBindings.push(node);
15491
15641
  if (node.type === "DeferStatement") defers.push(node);
15492
15642
  if (node.type === "TwillSwitchExpression") switches.push(node);
15493
- for (const [key, value] of Object.entries(node)) {
15643
+ if (node.type === "TwillEnumDeclaration") enums.push(node);
15644
+ for (const key of Object.keys(node)) {
15494
15645
  if (key === "trailing" || key === "loc") continue;
15495
- if (Array.isArray(value)) value.forEach(visit);
15646
+ const value = node[key];
15647
+ if (Array.isArray(value)) for (const child of value) visit(child);
15496
15648
  else if (value?.type) visit(value);
15497
15649
  }
15498
15650
  };
@@ -15501,8 +15653,10 @@ function parse$1(source, language = "ts", sourceType = "module") {
15501
15653
  ast,
15502
15654
  calls,
15503
15655
  guards,
15656
+ ifBindings,
15504
15657
  defers,
15505
15658
  switches,
15659
+ enums,
15506
15660
  comments,
15507
15661
  closures: parser.closures.filter((closure) => live.has(closure.node)).map((closure) => ({
15508
15662
  ...closure,
@@ -15511,6 +15665,11 @@ function parse$1(source, language = "ts", sourceType = "module") {
15511
15665
  };
15512
15666
  }
15513
15667
  //#endregion
15668
+ //#region src/defer-helper.ts
15669
+ function syncDeferBody(names) {
15670
+ 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";
15671
+ }
15672
+ //#endregion
15514
15673
  //#region src/defer.ts
15515
15674
  var functions = /* @__PURE__ */ new Set([
15516
15675
  "FunctionDeclaration",
@@ -15518,16 +15677,16 @@ var functions = /* @__PURE__ */ new Set([
15518
15677
  "ArrowFunctionExpression"
15519
15678
  ]);
15520
15679
  function children(node, visit) {
15521
- for (const [key, value] of Object.entries(node)) {
15680
+ for (const key of Object.keys(node)) {
15522
15681
  if (key === "loc" || key === "trailing") continue;
15523
- if (Array.isArray(value)) value.forEach((child) => {
15524
- if (child?.type) visit(child);
15525
- });
15526
- else if (value?.type) visit(value);
15682
+ const value = node[key];
15683
+ if (Array.isArray(value)) {
15684
+ for (const child of value) if (child?.type) visit(child);
15685
+ } else if (value?.type) visit(value);
15527
15686
  }
15528
15687
  }
15529
- /** Scope-local cleanup callbacks; dynamic registrations use lazy stacks. */
15530
- function lowerDefers(source, ast, code, language, usedNames, contentStarts, comments, fail) {
15688
+ /** Native finally where scope permits; callbacks retain dynamic registration semantics. */
15689
+ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comments, runtime, sourceType, fail) {
15531
15690
  const scopes = [];
15532
15691
  const visit = (node, scope, parent) => {
15533
15692
  if (functions.has(node.type) || [
@@ -15576,10 +15735,40 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
15576
15735
  }
15577
15736
  return start;
15578
15737
  };
15738
+ let sharedHelper;
15579
15739
  for (const scope of scopes) {
15580
15740
  if (!scope.defers.length) continue;
15581
15741
  const { block } = scope;
15582
15742
  const statements = block.body;
15743
+ const first = statements.indexOf(scope.defers[0]);
15744
+ let native = first >= 0 && scope.defers.every((node) => !node.awaited && statements.includes(node));
15745
+ if (native) {
15746
+ native = !statements.some((node, index) => node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction" || node.type === "VariableDeclaration" && node.kind.endsWith("using") || index > first && (node.type === "VariableDeclaration" && node.kind !== "var" || node.type.endsWith("Declaration") && node.type !== "VariableDeclaration" || node.type === "GuardStatement" && node.binding));
15747
+ const check = (node) => {
15748
+ if (node.type === "WithStatement" || node.type === "ThisExpression" || node.type === "VariableDeclarator" && !node.init || node.type === "VariableDeclaration" && node.kind === "var" && (!statements.includes(node) || node.start > scope.defers[0].start) || node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval") native = false;
15749
+ children(node, check);
15750
+ };
15751
+ if (native) check(block);
15752
+ const cleanupScope = (node) => {
15753
+ if (node.type === "FunctionDeclaration" || node.directive || node.type === "VariableDeclaration" && node.kind === "var") native = false;
15754
+ if (!functions.has(node.type)) children(node, cleanupScope);
15755
+ };
15756
+ if (native) for (const node of scope.defers) cleanupScope(node.cleanup.body);
15757
+ }
15758
+ if (native) {
15759
+ for (const node of [...scope.defers].reverse()) {
15760
+ code.overwrite(node.start, node.cleanup.body.start, ";try {");
15761
+ if (!node.cleanup.body.body.length) {
15762
+ code.remove(node.cleanup.body.start, node.cleanup.body.end);
15763
+ code.appendLeft(block.end - 1, "\n} finally " + source.slice(node.cleanup.body.start, node.cleanup.body.end) + "\n");
15764
+ continue;
15765
+ }
15766
+ code.prependRight(node.cleanup.body.start, "\n} finally ");
15767
+ code.appendLeft(node.cleanup.body.end, "\n");
15768
+ if (node.cleanup.body.end !== block.end - 1) code.move(node.cleanup.body.start, node.cleanup.body.end, block.end - 1);
15769
+ }
15770
+ continue;
15771
+ }
15583
15772
  const single = scope.defers.length === 1 && statements.includes(scope.defers[0]);
15584
15773
  const stack = fresh(single ? "Cleanup" : "Defers");
15585
15774
  const failure = single ? "" : fresh("Failure");
@@ -15600,7 +15789,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
15600
15789
  const declarations = scope.functionBody ? ordinary.filter((node) => node.type === "FunctionDeclaration") : [];
15601
15790
  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.");
15602
15791
  const names = [...new Set(declarations.map((node) => node.id.name))];
15603
- code.appendLeft(start, `;${jsdoc}let ${stack}${annotation};${names.length ? `var ${names.join(",")};` : ""}try {\n`);
15792
+ code.prependLeft(start, `;${jsdoc}let ${stack}${annotation};${names.length ? `var ${names.join(",")};` : ""}try {\n`);
15604
15793
  let anchor = start;
15605
15794
  const initial = /* @__PURE__ */ new Set();
15606
15795
  for (const statement of ordinary) {
@@ -15624,11 +15813,37 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
15624
15813
  }
15625
15814
  if (single) {
15626
15815
  const call = allAsync ? `if (${stack}) await ${stack}();` : `${stack}?.();`;
15627
- code.prependLeft(block.end - 1, `\n} finally { ${call} }\n`);
15816
+ code.appendLeft(block.end - 1, `\n} finally { ${call} }\n`);
15817
+ continue;
15818
+ }
15819
+ if (!anyAsync) {
15820
+ let body;
15821
+ if (runtime === "external") {
15822
+ if (sourceType !== "module") fail(scope.defers[0], "External runtime helpers require module sourceType; use runtime: inline for scripts.");
15823
+ sharedHelper ??= fresh("RunDefers");
15824
+ body = `${sharedHelper}(${stack});`;
15825
+ } else body = syncDeferBody({
15826
+ stack,
15827
+ failed,
15828
+ failure,
15829
+ cleanup: callback,
15830
+ error
15831
+ });
15832
+ code.appendLeft(block.end - 1, `} finally { ${body} }`);
15628
15833
  continue;
15629
15834
  }
15630
15835
  const call = mixed ? `if (${callback}.async) await ${callback}.run(); else ${callback}.run();` : `${allAsync ? "await " : ""}${callback}();`;
15631
- 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}; }`);
15836
+ 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}; }`);
15837
+ }
15838
+ if (sharedHelper) {
15839
+ let start = ast.body.find((node) => !node.directive).start;
15840
+ for (const comment of sortedComments) {
15841
+ if (comment.end > start) continue;
15842
+ if (/\S/.test(source.slice(comment.end, start))) break;
15843
+ 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;
15844
+ else break;
15845
+ }
15846
+ code.prependLeft(start, `\nimport { runDefers as ${sharedHelper} } from '@swiftuijs/twill-runtime/helpers/v1';\n`);
15632
15847
  }
15633
15848
  }
15634
15849
  //#endregion
@@ -15652,22 +15867,68 @@ function suspension(node) {
15652
15867
  }
15653
15868
  }
15654
15869
  }
15655
- /** Native switches with per-arm scopes; expression contexts use a synchronous
15656
- * lexical IIFE. Direct returns need no extra function or scheduling. */
15657
- function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, fail) {
15870
+ /** Native switches with per-arm scopes. Direct returns and standalone variable
15871
+ * initializers need no extra function; other expressions keep a lexical IIFE. */
15872
+ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail) {
15658
15873
  const returns = /* @__PURE__ */ new Set();
15659
- const visit = (node) => {
15874
+ const initializers = /* @__PURE__ */ new Map();
15875
+ const leadingDoc = (start) => {
15876
+ let low = 0, high = comments.length;
15877
+ while (low < high) {
15878
+ const middle = low + high >>> 1;
15879
+ if (comments[middle].end <= start) low = middle + 1;
15880
+ else high = middle;
15881
+ }
15882
+ const comment = comments[low - 1];
15883
+ return comment && /^\s*$/.test(source.slice(comment.end, start)) ? comment : void 0;
15884
+ };
15885
+ const directEval = (node) => {
15886
+ if ([
15887
+ "FunctionDeclaration",
15888
+ "FunctionExpression",
15889
+ "ArrowFunctionExpression"
15890
+ ].includes(node.type)) return false;
15891
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
15892
+ return Object.entries(node).some(([key, value]) => ![
15893
+ "loc",
15894
+ "trailing",
15895
+ "tag"
15896
+ ].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
15897
+ };
15898
+ const grouping = (start, end, initializer = false) => {
15899
+ const parentheses = [];
15900
+ const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
15901
+ for (;;) {
15902
+ const token = tokens.getToken();
15903
+ if (token.type === types$1.eof) break;
15904
+ if (initializer && token.type === types$1.eq) parentheses.length = 0;
15905
+ if (token.type === types$1.parenL || token.type === types$1.parenR) parentheses.push(token);
15906
+ }
15907
+ for (const token of parentheses) code.remove(start + token.start, start + token.end);
15908
+ };
15909
+ const visit = (node, parent) => {
15910
+ if (node.type === "VariableDeclaration" && node.declarations.length === 1) {
15911
+ const declaration = node.declarations[0];
15912
+ if (declaration.id.type === "Identifier" && declaration.init?.type === "TwillSwitchExpression" && [
15913
+ "Program",
15914
+ "BlockStatement",
15915
+ "SwitchCase",
15916
+ "ExportNamedDeclaration"
15917
+ ].includes(parent?.type ?? "")) {
15918
+ const statement = parent?.type === "ExportNamedDeclaration" ? parent : node;
15919
+ const comment = leadingDoc(statement.start);
15920
+ const prefixStart = comment && source.startsWith("/**", comment.start) ? comment.start : statement.start;
15921
+ 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, {
15922
+ declaration,
15923
+ statement,
15924
+ prefixStart
15925
+ });
15926
+ }
15927
+ }
15660
15928
  if (node.type === "ReturnStatement" && node.argument?.type === "TwillSwitchExpression") {
15661
15929
  returns.add(node.argument);
15662
15930
  code.remove(node.start, node.start + 6);
15663
- for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) {
15664
- const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
15665
- for (;;) {
15666
- const token = tokens.getToken();
15667
- if (token.type === types$1.eof) break;
15668
- if (token.type === types$1.parenL || token.type === types$1.parenR) code.remove(start + token.start, start + token.end);
15669
- }
15670
- }
15931
+ for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) grouping(start, end);
15671
15932
  }
15672
15933
  for (const [key, value] of Object.entries(node)) {
15673
15934
  if ([
@@ -15675,7 +15936,7 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
15675
15936
  "trailing",
15676
15937
  "tag"
15677
15938
  ].includes(key)) continue;
15678
- for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child);
15939
+ for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child, node);
15679
15940
  }
15680
15941
  };
15681
15942
  visit(ast);
@@ -15691,26 +15952,84 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
15691
15952
  for (const node of switches) {
15692
15953
  const direct = returns.has(node);
15693
15954
  const suspended = !direct && suspension(node);
15694
- if (suspended) fail(suspended, "await/yield inside a switch expression requires a direct return. Bind the subject first, or await the whole result explicitly.");
15955
+ 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.`);
15956
+ const initializer = initializers.get(node);
15957
+ const result = initializer ? fresh("Result") : void 0;
15958
+ const exit = initializer ? fresh("Exit") : void 0;
15695
15959
  const subject = fresh("Subject");
15696
- code.appendLeft(node.start, `${direct ? "{" : "(() => {"}const ${subject} = `);
15960
+ if (initializer) {
15961
+ const { declaration, statement, prefixStart } = initializer;
15962
+ const annotation = language.startsWith("ts") && declaration.id.typeAnnotation ? `: typeof ${declaration.id.name}` : "";
15963
+ grouping(declaration.id.end, node.start, true);
15964
+ grouping(node.end, statement.end);
15965
+ code.appendLeft(prefixStart, `let ${result}${annotation}; ${exit}: {const ${subject} = `);
15966
+ if (prefixStart < statement.start) code.prependRight(prefixStart, "\n");
15967
+ code.move(prefixStart, node.start, node.end);
15968
+ code.appendRight(node.end, ` ${result}`);
15969
+ }
15970
+ const valuePrefix = result ? `${result} = (` : "return ";
15971
+ const valueSuffix = result ? `); break ${exit};` : ";";
15972
+ const kind = node.cases.some((branch) => branch.enumPattern) ? fresh("Kind") : void 0;
15973
+ if (!initializer) code.appendLeft(node.start, `${direct ? "{" : "(() => {"}const ${subject} = `);
15697
15974
  code.remove(node.parenStart, node.parenStart + 1);
15698
- code.move(node.start, node.start + 6, node.parenEnd);
15699
- code.appendLeft(node.parenEnd, "; ");
15700
- code.overwrite(node.parenEnd, node.parenEnd + 1, ` (${subject}${node.discriminator === void 0 ? "" : `[${JSON.stringify(node.discriminator)}]`})`);
15701
- for (const branch of node.cases) if (branch.pattern) {
15975
+ if (node.keyword === "match") {
15976
+ const tokens = Parser.tokenizer(source.slice(node.discriminant.end, node.parenEnd), { ecmaVersion: "latest" });
15977
+ for (;;) {
15978
+ const token = tokens.getToken();
15979
+ if (token.type === types$1.eof) break;
15980
+ if (token.type === types$1.comma) code.remove(node.discriminant.end + token.start, node.discriminant.end + token.end);
15981
+ }
15982
+ }
15983
+ const keywordEnd = node.start + (node.keyword === "match" ? 5 : 6);
15984
+ if (node.keyword === "match") code.overwrite(node.start, keywordEnd, "switch");
15985
+ code.move(node.start, keywordEnd, node.parenEnd);
15986
+ code.appendLeft(node.parenEnd, `; ${kind ? `const ${kind} = ${subject}["kind"]; ` : ""}`);
15987
+ code.overwrite(node.parenEnd, node.parenEnd + 1, ` (${kind ?? subject}${kind || node.discriminator === void 0 ? "" : `[${JSON.stringify(node.discriminator)}]`})`);
15988
+ for (const branch of node.cases) if (branch.enumPattern) {
15989
+ const { reference, binding, start, parenStart, parenEnd, explicitKeyword } = branch.enumPattern;
15990
+ const tag = JSON.stringify(reference.property.name);
15991
+ if (language.startsWith("ts")) {
15992
+ const witness = `(${tag} satisfies (typeof `;
15993
+ if (explicitKeyword) code.overwrite(start, start + 4, witness);
15994
+ else code.appendLeft(start, witness);
15995
+ code.appendLeft(reference.end, ` extends (...args: never[]) => { readonly kind: ${tag} } ? ${tag} : never))`);
15996
+ } else if (explicitKeyword) {
15997
+ code.overwrite(start, start + 4, tag);
15998
+ code.remove(reference.start, reference.end);
15999
+ } else code.overwrite(reference.start, reference.end, tag);
16000
+ code.remove(parenStart, parenStart + 1);
16001
+ code.remove(parenEnd, parenEnd + 1);
16002
+ if (binding) {
16003
+ code.appendLeft(binding.start, ": { const ");
16004
+ code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : valuePrefix}`);
16005
+ code.appendLeft(branch.valueEnd, `${branch.throw ? ";" : valueSuffix} }`);
16006
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
16007
+ } else if (!branch.throw) {
16008
+ code.appendLeft(branch.colonStart + 1, ` ${valuePrefix}`);
16009
+ if (result) {
16010
+ code.appendLeft(branch.valueEnd, valueSuffix);
16011
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
16012
+ }
16013
+ }
16014
+ } else if (branch.pattern) {
15702
16015
  const { pattern, tag } = branch;
15703
16016
  code.appendLeft(tag.value.start, fresh("Tag"));
15704
16017
  code.move(tag.value.start, tag.value.end, pattern.start);
15705
16018
  code.appendLeft(tag.value.end, ": { const ");
15706
- code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : "return "}`);
15707
- code.appendLeft(branch.valueEnd, "; }");
16019
+ code.overwrite(branch.colonStart, branch.colonStart + 1, ` = ${subject}; ${branch.throw ? "" : valuePrefix}`);
16020
+ code.appendLeft(branch.valueEnd, `${branch.throw ? ";" : valueSuffix} }`);
15708
16021
  if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
15709
- } else if (!branch.throw) code.appendLeft(branch.colonStart + 1, " return ");
15710
- const hasDefault = node.cases.some((branch) => !branch.pattern && branch.test === null);
15711
- const check = !hasDefault && language.startsWith("ts") ? ` ${subject} satisfies never;` : "";
16022
+ } else if (!branch.throw) {
16023
+ code.appendLeft(branch.colonStart + 1, ` ${valuePrefix}`);
16024
+ if (result) {
16025
+ code.appendLeft(branch.valueEnd, valueSuffix);
16026
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
16027
+ }
16028
+ }
16029
+ const hasDefault = node.cases.some((branch) => !branch.pattern && !branch.enumPattern && branch.test === null);
16030
+ const check = !hasDefault && language.startsWith("ts") ? ` ${kind ? `(0 as unknown as typeof ${kind})` : subject} satisfies never;` : "";
15712
16031
  const failure = hasDefault ? "" : `${check} throw new TypeError('Non-exhaustive switch expression');`;
15713
- code.overwrite(node.end - 1, node.end, `}${failure}${direct ? "}" : "})()"}`);
16032
+ code.overwrite(node.end - 1, node.end, `}${failure}${direct || initializer ? "}" : "})()"}`, { contentOnly: true });
15714
16033
  }
15715
16034
  }
15716
16035
  //#endregion
@@ -15842,7 +16161,7 @@ function normalizePath(url, type) {
15842
16161
  /**
15843
16162
  * Attempts to resolve `input` URL/path relative to `base`.
15844
16163
  */
15845
- function resolve$3(input, base) {
16164
+ function resolve$4(input, base) {
15846
16165
  if (!input && !base) return "";
15847
16166
  const url = parseUrl(input);
15848
16167
  let inputType = url.type;
@@ -15887,7 +16206,7 @@ function stripFilename(path) {
15887
16206
  function resolver(mapUrl, sourceRoot) {
15888
16207
  const from = stripFilename(mapUrl);
15889
16208
  const prefix = sourceRoot ? sourceRoot + "/" : "";
15890
- return (source) => resolve$3(prefix + (source || ""), from);
16209
+ return (source) => resolve$4(prefix + (source || ""), from);
15891
16210
  }
15892
16211
  var COLUMN$1 = 0;
15893
16212
  var SOURCES_INDEX$1 = 1;
@@ -16028,6 +16347,10 @@ var TraceMap = class {
16028
16347
  function cast$1(map) {
16029
16348
  return map;
16030
16349
  }
16350
+ function encodedMappings(map) {
16351
+ var _a, _b;
16352
+ return (_b = (_a = cast$1(map))._encoded) != null ? _b : _a._encoded = encode(cast$1(map)._decoded);
16353
+ }
16031
16354
  function decodedMappings(map) {
16032
16355
  var _a;
16033
16356
  return (_a = cast$1(map))._decoded || (_a._decoded = decode(cast$1(map)._encoded));
@@ -16113,11 +16436,193 @@ function generatedPosition$1(map, source, line, column, bias, all) {
16113
16436
  return GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]);
16114
16437
  }
16115
16438
  //#endregion
16116
- //#region src/compiler.ts
16439
+ //#region src/enums.ts
16440
+ /** Find free type references, respecting generic function/mapped/infer binders. */
16441
+ function references(node, names, found = /* @__PURE__ */ new Set(), bound = /* @__PURE__ */ new Set()) {
16442
+ const local = new Set(bound);
16443
+ for (const parameter of node.typeParameters?.params ?? []) local.add(parameter.name);
16444
+ if (node.type === "TSMappedType") local.add(node.typeParameter.name);
16445
+ if (node.type === "TSInferType") local.add(node.typeParameter.name);
16446
+ if (node.type === "TSTypeReference" && node.typeName.type === "Identifier") {
16447
+ const name = node.typeName.name;
16448
+ if (names.has(name) && !local.has(name)) found.add(name);
16449
+ }
16450
+ for (const [key, value] of Object.entries(node)) {
16451
+ if (["loc", "typeName"].includes(key)) continue;
16452
+ for (const child of Array.isArray(value) ? value : [value]) {
16453
+ if (!child?.type) continue;
16454
+ const childBound = new Set(local);
16455
+ if (node.type === "TSConditionalType" && key === "trueType") {
16456
+ const bind = (part) => {
16457
+ if (part.type === "TSInferType") childBound.add(part.typeParameter.name);
16458
+ for (const [property, nested] of Object.entries(part)) {
16459
+ if (property === "loc") continue;
16460
+ if (part.type === "TSConditionalType" && property === "extendsType") continue;
16461
+ for (const item of Array.isArray(nested) ? nested : [nested]) if (item?.type) bind(item);
16462
+ }
16463
+ };
16464
+ bind(node.extendsType);
16465
+ }
16466
+ references(child, names, found, childBound);
16467
+ }
16468
+ }
16469
+ return found;
16470
+ }
16471
+ function enumTypeParameters(node, branch) {
16472
+ const parameters = node.typeParameters?.params ?? [];
16473
+ const names = new Set(parameters.map((parameter) => parameter.name));
16474
+ const used = /* @__PURE__ */ new Set();
16475
+ for (const field of branch.params) references(field.typeAnnotation, names, used);
16476
+ for (let size = -1; size !== used.size;) {
16477
+ size = used.size;
16478
+ for (const parameter of parameters) {
16479
+ if (!used.has(parameter.name)) continue;
16480
+ if (parameter.constraint) references(parameter.constraint, names, used);
16481
+ if (parameter.default) references(parameter.default, names, used);
16482
+ }
16483
+ }
16484
+ return parameters.filter((parameter) => used.has(parameter.name));
16485
+ }
16486
+ /** Tagged records and precise factories; no application runtime is introduced. */
16487
+ function lowerEnums(source, ast, enums, code) {
16488
+ const insertions = [];
16489
+ const exported = /* @__PURE__ */ new Set();
16490
+ const visit = (node) => {
16491
+ if (node.type === "ExportNamedDeclaration") exported.add(node.declaration);
16492
+ for (const [key, value] of Object.entries(node)) {
16493
+ if (["loc", "trailing"].includes(key)) continue;
16494
+ for (const child of Array.isArray(value) ? value : [value]) if (child?.type) visit(child);
16495
+ }
16496
+ };
16497
+ visit(ast);
16498
+ for (const node of enums) {
16499
+ const replacement = {
16500
+ anchor: node.braceStart,
16501
+ before: false,
16502
+ text: "= ",
16503
+ copies: []
16504
+ };
16505
+ for (const [index, branch] of node.cases.entries()) {
16506
+ if (index) replacement.text += " | ";
16507
+ replacement.text += `{ readonly kind: ${JSON.stringify(branch.id.name)}; `;
16508
+ for (const field of branch.params) {
16509
+ replacement.text += "readonly ";
16510
+ const text = source.slice(field.start, field.typeAnnotation.end);
16511
+ replacement.copies.push({
16512
+ start: replacement.text.length,
16513
+ sourceStart: field.start,
16514
+ length: text.length
16515
+ });
16516
+ replacement.text += text + "; ";
16517
+ }
16518
+ replacement.text += "}";
16519
+ }
16520
+ replacement.text += `; ${exported.has(node) ? "export " : ""}const `;
16521
+ replacement.copies.push({
16522
+ start: replacement.text.length,
16523
+ sourceStart: node.id.start,
16524
+ length: node.id.end - node.id.start
16525
+ });
16526
+ replacement.text += source.slice(node.id.start, node.id.end) + " = {";
16527
+ insertions.push(replacement);
16528
+ code.overwrite(node.start, node.start + 4, "type");
16529
+ code.overwrite(node.braceStart, node.braceStart + 1, replacement.text);
16530
+ for (const branch of node.cases) {
16531
+ code.remove(branch.start, branch.start + 4);
16532
+ const parameters = enumTypeParameters(node, branch);
16533
+ const copied = {
16534
+ anchor: branch.id.end,
16535
+ before: true,
16536
+ text: parameters.length ? "<" : "",
16537
+ copies: []
16538
+ };
16539
+ for (const [index, parameter] of parameters.entries()) {
16540
+ if (index) copied.text += ", ";
16541
+ const text = source.slice(parameter.start, parameter.end);
16542
+ copied.copies.push({
16543
+ start: copied.text.length,
16544
+ sourceStart: parameter.start,
16545
+ length: text.length
16546
+ });
16547
+ copied.text += text;
16548
+ }
16549
+ if (parameters.length) copied.text += ">";
16550
+ const generic = copied.text;
16551
+ if (generic) insertions.push(copied);
16552
+ code.appendLeft(branch.id.end, generic + (branch.hasParens ? "" : "()"));
16553
+ const fields = branch.params.map((field) => field.name);
16554
+ code.appendLeft(branch.valueEnd, `{ return { kind: ${JSON.stringify(branch.id.name)}${fields.length ? ", " + fields.join(", ") : ""} } as const; },`);
16555
+ if (source[branch.end - 1] === ";") code.remove(branch.end - 1, branch.end);
16556
+ }
16557
+ code.appendLeft(node.end, ";");
16558
+ }
16559
+ return insertions;
16560
+ }
16561
+ /** Generated type copies map to their actual source tokens, including .d.ts maps. */
16562
+ function mapEnumCopies(source, generated, map, insertions) {
16563
+ const starts = (text) => {
16564
+ const result = [0];
16565
+ for (let index = 0; index < text.length; index++) if (text[index] === "\n") result.push(index + 1);
16566
+ return result;
16567
+ };
16568
+ const originalLines = starts(source), generatedLines = starts(generated);
16569
+ const position = (lines, offset) => {
16570
+ let line = 0;
16571
+ while (line + 1 < lines.length && lines[line + 1] <= offset) line++;
16572
+ return {
16573
+ line,
16574
+ column: offset - lines[line]
16575
+ };
16576
+ };
16577
+ const trace = new TraceMap(map);
16578
+ const decoded = decodedMappings(trace);
16579
+ const copies = [];
16580
+ for (const insertion of insertions) {
16581
+ const anchor = position(originalLines, insertion.anchor);
16582
+ const location = generatedPositionFor(trace, {
16583
+ source: map.sources[0],
16584
+ line: anchor.line + 1,
16585
+ column: anchor.column
16586
+ });
16587
+ const offset = generatedLines[location.line - 1] + location.column - (insertion.before ? insertion.text.length : 0);
16588
+ if (generated.slice(offset, offset + insertion.text.length) !== insertion.text) throw new Error("Enum mapping anchor does not match generated text.");
16589
+ for (const copy of insertion.copies) {
16590
+ copies.push({
16591
+ generatedStart: offset + copy.start,
16592
+ sourceStart: copy.sourceStart,
16593
+ length: copy.length
16594
+ });
16595
+ for (let index = 0; index < copy.length; index++) {
16596
+ const target = position(generatedLines, offset + copy.start + index);
16597
+ const original = position(originalLines, copy.sourceStart + index);
16598
+ const line = decoded[target.line];
16599
+ const entry = [
16600
+ target.column,
16601
+ 0,
16602
+ original.line,
16603
+ original.column
16604
+ ];
16605
+ const existing = line.findIndex((segment) => segment[0] === target.column);
16606
+ if (existing >= 0) line[existing] = entry;
16607
+ else line.push(entry);
16608
+ }
16609
+ }
16610
+ }
16611
+ for (const line of decoded) line.sort((left, right) => left[0] - right[0]);
16612
+ map.mappings = encodedMappings(new TraceMap({
16613
+ ...JSON.parse(map.toString()),
16614
+ mappings: decoded
16615
+ }));
16616
+ return copies;
16617
+ }
16618
+ //#endregion
16619
+ //#region src/extensions.ts
16117
16620
  var extensions = [".twill", ".twillx"];
16118
16621
  function isTwillFile(id) {
16119
16622
  return extensions.some((extension) => id.split(/[?#]/, 1)[0].endsWith(extension));
16120
16623
  }
16624
+ //#endregion
16625
+ //#region src/compiler.ts
16121
16626
  function inferLanguage(filename) {
16122
16627
  if (/\.(?:twillx|tsx)$/.test(filename)) return "tsx";
16123
16628
  if (/\.jsx$/.test(filename)) return "jsx";
@@ -16144,12 +16649,12 @@ var TwillSyntaxError = class extends SyntaxError {
16144
16649
  };
16145
16650
  function walk(node, visit) {
16146
16651
  visit(node);
16147
- for (const [key, value] of Object.entries(node)) {
16148
- if (["loc", "trailing"].includes(key)) continue;
16149
- if (Array.isArray(value)) value.forEach((child) => {
16150
- if (child?.type) walk(child, visit);
16151
- });
16152
- else if (value?.type) walk(value, visit);
16652
+ for (const key of Object.keys(node)) {
16653
+ if (key === "loc" || key === "trailing") continue;
16654
+ const value = node[key];
16655
+ if (Array.isArray(value)) {
16656
+ for (const child of value) if (child?.type) walk(child, visit);
16657
+ } else if (value?.type) walk(value, visit);
16153
16658
  }
16154
16659
  }
16155
16660
  function calleeName(node) {
@@ -16160,6 +16665,7 @@ function calleeName(node) {
16160
16665
  }
16161
16666
  }
16162
16667
  function transform(source, options = {}) {
16668
+ if (options.runtime !== void 0 && !["inline", "external"].includes(options.runtime)) throw new TypeError("runtime must be inline or external");
16163
16669
  const filename = options.filename ?? "input.twill";
16164
16670
  let parsed;
16165
16671
  try {
@@ -16168,11 +16674,28 @@ function transform(source, options = {}) {
16168
16674
  throw new TwillSyntaxError(source, filename, error);
16169
16675
  }
16170
16676
  const code = new MagicString(source);
16677
+ const enumInsertions = parsed.enums.length ? lowerEnums(source, parsed.ast, parsed.enums, code) : [];
16171
16678
  const usedNames = /* @__PURE__ */ new Set();
16172
- 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) => {
16173
- if (node.type === "Identifier") usedNames.add(node.name);
16679
+ 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) => {
16680
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
16174
16681
  });
16175
16682
  let guardCounter = 0;
16683
+ const ifBindingStarts = new Set(parsed.ifBindings.map((node) => node.consequent.start + 1));
16684
+ for (const statement of parsed.ifBindings) {
16685
+ const binding = statement.binding.declarations[0].id;
16686
+ let temporary;
16687
+ do
16688
+ temporary = `__twillIf${guardCounter++}`;
16689
+ while (usedNames.has(temporary));
16690
+ usedNames.add(temporary);
16691
+ const patternEnd = binding.typeAnnotation?.start ?? binding.end;
16692
+ code.overwrite(statement.start, binding.start, `{const ${temporary}`);
16693
+ code.move(binding.start, patternEnd, statement.consequent.start + 1);
16694
+ code.prependRight(binding.start, "const ");
16695
+ code.appendLeft(patternEnd, ` = ${temporary};`);
16696
+ code.prependLeft(statement.consequent.start, `; if (${temporary} !== null && ${temporary} !== void 0) `);
16697
+ code.appendLeft(statement.end, "}");
16698
+ }
16176
16699
  for (const guard of parsed.guards) {
16177
16700
  if (guard.singleStatement) {
16178
16701
  code.prependLeft(guard.start, "{");
@@ -16379,13 +16902,15 @@ function transform(source, options = {}) {
16379
16902
  const collect = (statement) => {
16380
16903
  switch (statement.type) {
16381
16904
  case "ExpressionStatement":
16382
- code.prependLeft(statement.start, `${collector}.push(`);
16905
+ if (ifBindingStarts.has(statement.start)) code.appendRight(statement.start, `${collector}.push(`);
16906
+ else code.prependLeft(statement.start, `${collector}.push(`);
16383
16907
  code.appendLeft(source[statement.end - 1] === ";" ? statement.end - 1 : statement.end, ")");
16384
16908
  break;
16385
16909
  case "BlockStatement":
16386
16910
  statement.body.forEach(collect);
16387
16911
  break;
16388
16912
  case "IfStatement":
16913
+ case "TwillIfBinding":
16389
16914
  collect(statement.consequent);
16390
16915
  if (statement.alternate) collect(statement.alternate);
16391
16916
  break;
@@ -16429,7 +16954,7 @@ function transform(source, options = {}) {
16429
16954
  if (component) code.prependLeft(closures.at(-1).end, (vue ? "})}" : directChild || metadataByNode.get(closures[0]).header ? ")}" : ")()}") + `</${calleeName(node.callee)}>)`);
16430
16955
  else code.appendLeft(closures.at(-1).end, ")");
16431
16956
  }
16432
- if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, (node, message) => {
16957
+ if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, parsed.comments, (node, message) => {
16433
16958
  throw new TwillSyntaxError(source, filename, {
16434
16959
  message,
16435
16960
  pos: node.start,
@@ -16440,7 +16965,7 @@ function transform(source, options = {}) {
16440
16965
  if (parsed.defers.length) {
16441
16966
  const starts = /* @__PURE__ */ new Map();
16442
16967
  for (const metadata of parsed.closures) if (metadata.header) starts.set(metadata.node.body, metadata.header.end);
16443
- lowerDefers(source, parsed.ast, code, options.language ?? inferLanguage(filename), usedNames, starts, parsed.comments, (node, message) => {
16968
+ lowerDefers(source, parsed.ast, code, options.language ?? inferLanguage(filename), usedNames, starts, parsed.comments, options.runtime ?? "inline", options.sourceType ?? "module", (node, message) => {
16444
16969
  throw new TwillSyntaxError(source, filename, {
16445
16970
  message,
16446
16971
  pos: node.start,
@@ -16454,14 +16979,19 @@ function transform(source, options = {}) {
16454
16979
  includeContent: true,
16455
16980
  hires: true
16456
16981
  });
16982
+ const enumCopies = enumInsertions.length ? mapEnumCopies(source, code.toString(), map, enumInsertions) : [];
16457
16983
  return {
16458
16984
  code: code.toString(),
16459
16985
  map,
16460
16986
  changed: code.hasChanged(),
16461
16987
  closures: parsed.closures.length,
16462
16988
  guards: parsed.guards.length,
16989
+ ifBindings: parsed.ifBindings.length,
16463
16990
  defers: parsed.defers.length,
16464
16991
  switches: parsed.switches.length,
16992
+ associatedEnums: parsed.enums,
16993
+ enumPatterns: parsed.switches.flatMap((node) => node.cases.flatMap((branch) => branch.enumPattern ? [branch.enumPattern] : [])),
16994
+ enumCopies,
16465
16995
  componentProps
16466
16996
  };
16467
16997
  }
@@ -16490,21 +17020,78 @@ function generatedPosition(result, line, column) {
16490
17020
  });
16491
17021
  }
16492
17022
  //#endregion
16493
- //#region src/config.ts
17023
+ //#region src/compilation-cache.ts
17024
+ var digest = (source) => (0, node_crypto.createHash)("sha256").update(source).digest("hex");
17025
+ function observe(path, kind) {
17026
+ try {
17027
+ if (kind === "directory") return (0, node_fs.statSync)(path).isDirectory() ? "directory" : "absent";
17028
+ return digest((0, node_fs.readFileSync)(path));
17029
+ } catch (error) {
17030
+ return ["ENOENT", "ENOTDIR"].includes(error.code ?? "") ? "absent" : "unavailable";
17031
+ }
17032
+ }
17033
+ //#endregion
17034
+ //#region src/configuration.ts
16494
17035
  var jsxConfigs = /* @__PURE__ */ new Map();
17036
+ function record(observations, path, kind) {
17037
+ path = (0, node_path.resolve)(path);
17038
+ observations.set(kind + path, {
17039
+ path,
17040
+ kind,
17041
+ value: observe(path, kind)
17042
+ });
17043
+ }
16495
17044
  /** Use the project's standard JSX setting, including extended tsconfigs. */
16496
- function jsxSource(root, configFile) {
16497
- const filename = configFile ?? typescript.default.findConfigFile(root, typescript.default.sys.fileExists);
17045
+ function jsxSource(root, configFile, observations) {
17046
+ const system = observations ? {
17047
+ ...typescript.default.sys,
17048
+ fileExists(path) {
17049
+ record(observations, path, "file");
17050
+ return typescript.default.sys.fileExists(path);
17051
+ },
17052
+ directoryExists(path) {
17053
+ record(observations, path, "directory");
17054
+ return typescript.default.sys.directoryExists(path);
17055
+ }
17056
+ } : typescript.default.sys;
17057
+ const filename = configFile ?? typescript.default.findConfigFile(root, system.fileExists);
16498
17058
  if (!filename) return void 0;
16499
17059
  const cached = jsxConfigs.get(filename);
16500
- if (cached && [...cached.files].every(([file, time]) => ((0, node_fs.existsSync)(file) ? (0, node_fs.statSync)(file).mtimeMs : -1) === time)) return cached.source;
17060
+ if (!observations && cached && [...cached.files].every(([file, time]) => ((0, node_fs.existsSync)(file) ? (0, node_fs.statSync)(file).mtimeMs : -1) === time)) return cached.source;
16501
17061
  const files = /* @__PURE__ */ new Map();
16502
17062
  const source = typescript.default.getParsedCommandLineOfConfigFile(filename, {}, {
16503
- ...typescript.default.sys,
17063
+ ...system,
16504
17064
  readDirectory: () => [],
16505
17065
  readFile(file) {
16506
17066
  files.set(file, (0, node_fs.existsSync)(file) ? (0, node_fs.statSync)(file).mtimeMs : -1);
16507
- return typescript.default.sys.readFile(file);
17067
+ if (!observations) return typescript.default.sys.readFile(file);
17068
+ const path = (0, node_path.resolve)(file);
17069
+ let bytes;
17070
+ try {
17071
+ bytes = (0, node_fs.readFileSync)(file);
17072
+ } catch {
17073
+ observations.set("file" + path, {
17074
+ path,
17075
+ kind: "file",
17076
+ value: "unavailable"
17077
+ });
17078
+ return;
17079
+ }
17080
+ observations.set("file" + path, {
17081
+ path,
17082
+ kind: "file",
17083
+ value: digest(bytes)
17084
+ });
17085
+ if (bytes[0] === 254 && bytes[1] === 255) {
17086
+ for (let i = 0; i < (bytes.length & -2); i += 2) {
17087
+ const byte = bytes[i];
17088
+ bytes[i] = bytes[i + 1];
17089
+ bytes[i + 1] = byte;
17090
+ }
17091
+ return bytes.toString("utf16le", 2);
17092
+ }
17093
+ if (bytes[0] === 255 && bytes[1] === 254) return bytes.toString("utf16le", 2);
17094
+ return bytes.toString("utf8", bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0);
16508
17095
  },
16509
17096
  getCurrentDirectory: () => root,
16510
17097
  onUnRecoverableConfigFileDiagnostic() {}
@@ -16515,19 +17102,35 @@ function jsxSource(root, configFile) {
16515
17102
  });
16516
17103
  return source;
16517
17104
  }
16518
- function loadConfig(root = process.cwd(), configFile) {
17105
+ function readConfig(root, configFile, observations) {
16519
17106
  const filename = (0, node_path.resolve)(root, "twill.config.json");
16520
- const jsxImportSource = jsxSource(root, configFile);
17107
+ const jsxImportSource = jsxSource(root, configFile, observations);
17108
+ if (observations) record(observations, filename, "file");
16521
17109
  if (!(0, node_fs.existsSync)(filename)) return { jsxImportSource };
16522
- const value = JSON.parse((0, node_fs.readFileSync)(filename, "utf8"));
17110
+ const text = (0, node_fs.readFileSync)(filename, "utf8");
17111
+ if (observations) observations.set("file" + filename, {
17112
+ path: filename,
17113
+ kind: "file",
17114
+ value: digest(text)
17115
+ });
17116
+ const value = JSON.parse(text);
16523
17117
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${filename}: expected an object`);
16524
17118
  if (value.implicitReturn !== void 0 && typeof value.implicitReturn !== "boolean") throw new Error(`${filename}: implicitReturn must be a boolean`);
16525
- for (const key of Object.keys(value)) if (!["implicitReturn", "$schema"].includes(key)) throw new Error(`${filename}: unknown option ${key}`);
17119
+ if (value.runtime !== void 0 && !["inline", "external"].includes(value.runtime)) throw new Error(`${filename}: runtime must be inline or external`);
17120
+ for (const key of Object.keys(value)) if (![
17121
+ "implicitReturn",
17122
+ "runtime",
17123
+ "$schema"
17124
+ ].includes(key)) throw new Error(`${filename}: unknown option ${key}`);
16526
17125
  return {
16527
17126
  implicitReturn: value.implicitReturn,
16528
- jsxImportSource
17127
+ jsxImportSource,
17128
+ ...value.runtime === void 0 ? {} : { runtime: value.runtime }
16529
17129
  };
16530
17130
  }
17131
+ function loadConfig(root = process.cwd(), configFile) {
17132
+ return readConfig(root, configFile);
17133
+ }
16531
17134
  //#endregion
16532
17135
  //#region ../../node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs
16533
17136
  var SetArray = class {
@@ -16828,7 +17431,7 @@ function remapping(input, loader, options) {
16828
17431
  //#endregion
16829
17432
  //#region src/recovery.ts
16830
17433
  /** Editor-only recovery; build and CLI compilation always use strict parsing. */
16831
- function recoverTransform(source, options) {
17434
+ function recoverTransform(source, options, onSyntaxError) {
16832
17435
  const repaired = new MagicString(source);
16833
17436
  let text = source;
16834
17437
  for (let attempt = 0; attempt < 8; attempt++) try {
@@ -16839,13 +17442,37 @@ function recoverTransform(source, options) {
16839
17442
  includeContent: true,
16840
17443
  hires: true
16841
17444
  });
17445
+ const repairedResult = {
17446
+ ...result,
17447
+ map: repairMap
17448
+ };
17449
+ const starts = [0];
17450
+ for (const match of source.matchAll(/\n/g)) starts.push(match.index + 1);
17451
+ const sourceOffset = (offset) => {
17452
+ const prefix = text.slice(0, offset).split("\n");
17453
+ const point = originalPosition(repairedResult, prefix.length, prefix.at(-1).length);
17454
+ return starts[point.line - 1] + point.column;
17455
+ };
17456
+ const enumPatterns = result.enumPatterns.map((pattern) => ({
17457
+ ...pattern,
17458
+ reference: {
17459
+ ...pattern.reference,
17460
+ property: {
17461
+ ...pattern.reference.property,
17462
+ start: sourceOffset(pattern.reference.property.start),
17463
+ end: sourceOffset(pattern.reference.property.end)
17464
+ }
17465
+ }
17466
+ }));
16842
17467
  const map = remapping([JSON.parse(result.map.toString()), JSON.parse(repairMap.toString())], () => null);
16843
17468
  return {
16844
17469
  ...result,
17470
+ enumPatterns,
16845
17471
  map
16846
17472
  };
16847
17473
  } catch (error) {
16848
17474
  if (!(error instanceof TwillSyntaxError)) throw error;
17475
+ if (attempt === 0) onSyntaxError?.(error);
16849
17476
  const before = text.slice(0, error.offset);
16850
17477
  let insertion;
16851
17478
  let offset = error.offset;
@@ -17110,13 +17737,18 @@ var TwillProject = class {
17110
17737
  result(filename, source) {
17111
17738
  const cached = this.results.get(filename);
17112
17739
  if (cached?.source === source) return cached.result;
17113
- const result = (this.recover ? recoverTransform : transform)(source, {
17740
+ const options = {
17114
17741
  ...this.options,
17115
17742
  filename
17116
- });
17743
+ };
17744
+ let syntaxError;
17745
+ const result = this.recover ? recoverTransform(source, options, (error) => {
17746
+ syntaxError = error;
17747
+ }) : transform(source, options);
17117
17748
  this.results.set(filename, {
17118
17749
  source,
17119
- result
17750
+ result,
17751
+ syntaxError
17120
17752
  });
17121
17753
  return result;
17122
17754
  }
@@ -17210,11 +17842,9 @@ var TwillProject = class {
17210
17842
  for (const file of files) {
17211
17843
  if (/\.d\.[cm]?ts$/.test(file)) continue;
17212
17844
  try {
17213
- if (this.recover && isTwillFile(sourceFilename(file))) transform(this.text(file) ?? "", {
17214
- ...this.options,
17215
- filename: sourceFilename(file)
17216
- });
17217
17845
  this.transformed(file);
17846
+ const syntaxError = this.results.get(sourceFilename(file))?.syntaxError;
17847
+ if (syntaxError) throw syntaxError;
17218
17848
  result.push(...[...this.service.getSyntacticDiagnostics(file), ...this.service.getSemanticDiagnostics(file)].map((error) => this.diagnostic(error)));
17219
17849
  } catch (error) {
17220
17850
  const syntax = error;
@@ -17241,6 +17871,7 @@ var TwillProject = class {
17241
17871
  var TwillEditor = class {
17242
17872
  project;
17243
17873
  attributeCache = /* @__PURE__ */ new WeakMap();
17874
+ descriptorCache = /* @__PURE__ */ new WeakMap();
17244
17875
  constructor(project) {
17245
17876
  this.project = project;
17246
17877
  }
@@ -17309,10 +17940,11 @@ var TwillEditor = class {
17309
17940
  const source = this.project.text(filename);
17310
17941
  if (source === void 0) return void 0;
17311
17942
  if (!isTwillFile(filename)) return span.start >= 0 && span.start + span.length <= source.length ? span : void 0;
17312
- const generated = this.project.transformed(filename)?.code;
17943
+ const transformed = this.project.transformed(filename);
17944
+ const generated = transformed?.code;
17313
17945
  if (generated === void 0) return void 0;
17314
17946
  const start = this.project.toOriginalOffset(filename, span.start);
17315
- if (start < 0 || start + span.length > source.length || this.project.toGeneratedOffset(filename, start) !== span.start || source.slice(start, start + span.length) !== generated.slice(span.start, span.start + span.length)) return void 0;
17947
+ if (start < 0 || start + span.length > source.length || this.project.toGeneratedOffset(filename, start) !== span.start && !transformed.enumCopies.some((copy) => span.start >= copy.generatedStart && span.start + span.length <= copy.generatedStart + copy.length && start === copy.sourceStart + span.start - copy.generatedStart || start >= copy.sourceStart && start + span.length <= copy.sourceStart + copy.length && this.project.toOriginalOffset(filename, span.start + span.length - 1) === start + span.length - 1) || source.slice(start, start + span.length) !== generated.slice(span.start, span.start + span.length)) return void 0;
17316
17948
  return {
17317
17949
  start,
17318
17950
  length: span.length
@@ -17400,8 +18032,77 @@ var TwillEditor = class {
17400
18032
  references(filename, position) {
17401
18033
  return this.referenceGroups(filename, position)?.flatMap((group) => group.references);
17402
18034
  }
18035
+ descriptorSymbols(program) {
18036
+ const cached = this.descriptorCache.get(program);
18037
+ if (cached) return cached;
18038
+ const symbols = /* @__PURE__ */ new Set();
18039
+ const checker = program.getTypeChecker();
18040
+ for (const file of program.getSourceFiles()) {
18041
+ const original = sourceFilename(file.fileName);
18042
+ if (!isTwillFile(original)) continue;
18043
+ let patterns;
18044
+ try {
18045
+ patterns = this.project.transformed(original)?.enumPatterns;
18046
+ } catch {
18047
+ continue;
18048
+ }
18049
+ if (!patterns?.length) continue;
18050
+ const offsets = new Set(patterns.map((pattern) => this.project.toGeneratedOffset(original, pattern.reference.property.start)));
18051
+ const visit = (node) => {
18052
+ if (typescript.default.isIdentifier(node) && offsets.has(node.getStart())) {
18053
+ const symbol = checker.getSymbolAtLocation(node);
18054
+ if (symbol) for (const root of checker.getRootSymbols(symbol)) symbols.add(root);
18055
+ }
18056
+ typescript.default.forEachChild(node, visit);
18057
+ };
18058
+ visit(file);
18059
+ }
18060
+ this.descriptorCache.set(program, symbols);
18061
+ return symbols;
18062
+ }
17403
18063
  renameInfo(filename, position) {
17404
- const info = this.project.service.getRenameInfo(virtualFilename(filename), this.project.toGeneratedOffset(filename, position), { allowRenameOfImportPath: false });
18064
+ const offset = this.project.toGeneratedOffset(filename, position);
18065
+ const definitions = this.project.service.getDefinitionAtPosition(virtualFilename(filename), offset);
18066
+ const program = this.project.service.getProgram();
18067
+ const descriptors = this.descriptorSymbols(program);
18068
+ for (const definition of definitions ?? []) {
18069
+ const file = sourceFilename(definition.fileName);
18070
+ const definitionFile = this.sourceFile(file);
18071
+ let descriptor = false;
18072
+ const visitDefinition = (node) => {
18073
+ if (definition.textSpan.start < node.getStart() || definition.textSpan.start >= node.end) return;
18074
+ if (typescript.default.isIdentifier(node) || typescript.default.isStringLiteral(node)) {
18075
+ const symbol = program.getTypeChecker().getSymbolAtLocation(node);
18076
+ descriptor = !!symbol && program.getTypeChecker().getRootSymbols(symbol).some((root) => descriptors.has(root));
18077
+ } else typescript.default.forEachChild(node, visitDefinition);
18078
+ };
18079
+ if (descriptors.size && definitionFile) visitDefinition(definitionFile);
18080
+ if (descriptor) return {
18081
+ canRename: false,
18082
+ localizedErrorMessage: "Renaming an enum-pattern case descriptor requires linked discriminator edits and is not supported yet."
18083
+ };
18084
+ const declarations = this.project.transformed(file)?.associatedEnums;
18085
+ if (!declarations?.length) continue;
18086
+ const sourceFile = this.sourceFile(file);
18087
+ let generatedEnum = false;
18088
+ const visit = (node) => {
18089
+ if (definition.textSpan.start < node.getStart() || definition.textSpan.start >= node.end) return;
18090
+ if ((typescript.default.isTypeAliasDeclaration(node) || typescript.default.isVariableDeclaration(node)) && typescript.default.isIdentifier(node.name)) {
18091
+ const start = this.project.toOriginalOffset(file, node.name.getStart());
18092
+ if (declarations.some((declaration) => declaration.id.start === start)) {
18093
+ generatedEnum = true;
18094
+ return;
18095
+ }
18096
+ }
18097
+ typescript.default.forEachChild(node, visit);
18098
+ };
18099
+ if (sourceFile) visit(sourceFile);
18100
+ if (generatedEnum) return {
18101
+ canRename: false,
18102
+ localizedErrorMessage: "Renaming associated-value enum declarations, cases, payload fields and type parameters is not supported yet."
18103
+ };
18104
+ }
18105
+ const info = this.project.service.getRenameInfo(virtualFilename(filename), offset, { allowRenameOfImportPath: false });
17405
18106
  if (!info.canRename) return info;
17406
18107
  const span = this.mapSpan(filename, info.triggerSpan);
17407
18108
  return span ? {