@swiftuijs/twill 0.3.0 → 0.4.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 (72) hide show
  1. package/README.md +8 -3
  2. package/bin/run.mjs +2 -2
  3. package/dist/branches.d.ts +7 -1
  4. package/dist/cli.js +7 -8
  5. package/dist/{compilation-cache-7SugqJ4n.js → compilation-cache-BrV71YKt.js} +2 -3
  6. package/dist/{compilation-cache-7SugqJ4n.js.map → compilation-cache-BrV71YKt.js.map} +1 -1
  7. package/dist/{compiler-CbNSBUI2.js → compiler-DI0H4d1R.js} +178 -66
  8. package/dist/compiler-DI0H4d1R.js.map +1 -0
  9. package/dist/compiler.d.ts +2 -0
  10. package/dist/config-la_KkjCS.js +1 -0
  11. package/dist/config.js +2 -2
  12. package/dist/{configuration-BVMf6Q_j.js → configuration-ppqhmuy4.js} +7 -5
  13. package/dist/configuration-ppqhmuy4.js.map +1 -0
  14. package/dist/configuration.d.ts +1 -1
  15. package/dist/declarations-DXtb1AHP.js +131 -0
  16. package/dist/declarations-DXtb1AHP.js.map +1 -0
  17. package/dist/declarations.js +1 -132
  18. package/dist/doctor-BHwU_YBw.js +200 -0
  19. package/dist/doctor-BHwU_YBw.js.map +1 -0
  20. package/dist/doctor.js +1 -202
  21. package/dist/editor.d.ts +1 -0
  22. package/dist/editor.js +31 -5
  23. package/dist/editor.js.map +1 -1
  24. package/dist/esbuild.js +2 -3
  25. package/dist/{extensions-B1xTkoYP.js → extensions-7ixttcoK.js} +2 -3
  26. package/dist/{extensions-B1xTkoYP.js.map → extensions-7ixttcoK.js.map} +1 -1
  27. package/dist/{files-BW9geU0_.js → files-CgwJJ992.js} +3 -4
  28. package/dist/{files-BW9geU0_.js.map → files-CgwJJ992.js.map} +1 -1
  29. package/dist/index.js +2 -2
  30. package/dist/{loader-compiler-D1teTrXw.js → loader-compiler.js} +4 -5
  31. package/dist/loader-compiler.js.map +1 -0
  32. package/dist/loader-registration-BO43rvVU.js +14 -0
  33. package/dist/loader-registration-BO43rvVU.js.map +1 -0
  34. package/dist/loader-registration.d.ts +4 -0
  35. package/dist/loader-registration.js +1 -0
  36. package/dist/loader-state-DB7sONR_.js +60 -0
  37. package/dist/loader-state-DB7sONR_.js.map +1 -0
  38. package/dist/loader-state.d.ts +34 -0
  39. package/dist/loader-sync.d.ts +3 -0
  40. package/dist/loader-sync.js +27 -0
  41. package/dist/loader-sync.js.map +1 -0
  42. package/dist/loader.d.ts +2 -4
  43. package/dist/loader.js +10 -48
  44. package/dist/loader.js.map +1 -1
  45. package/dist/{plugin-BHfiQ2L-.js → plugin-DDOHhv7Z.js} +6 -7
  46. package/dist/{plugin-BHfiQ2L-.js.map → plugin-DDOHhv7Z.js.map} +1 -1
  47. package/dist/project-DYXf2Kgt.js +442 -0
  48. package/dist/project-DYXf2Kgt.js.map +1 -0
  49. package/dist/project.js +2 -443
  50. package/dist/register.js +3 -4
  51. package/dist/register.js.map +1 -1
  52. package/dist/rollup.js +2 -3
  53. package/dist/rspack.js +2 -3
  54. package/dist/syntax.js +2 -3
  55. package/dist/{transpile-D8P8Lsbx.js → transpile--6CFGUm0.js} +3 -4
  56. package/dist/{transpile-D8P8Lsbx.js.map → transpile--6CFGUm0.js.map} +1 -1
  57. package/dist/typescript-plugin.cjs +252 -128
  58. package/dist/typescript-plugin.cjs.map +1 -1
  59. package/dist/vite-D0Ct4uy4.js +6 -0
  60. package/dist/vite-D0Ct4uy4.js.map +1 -0
  61. package/dist/vite-react.js +2 -3
  62. package/dist/vite.js +1 -7
  63. package/dist/webpack.js +2 -3
  64. package/package.json +3 -3
  65. package/schemas/twill.schema.json +6 -0
  66. package/dist/compiler-CbNSBUI2.js.map +0 -1
  67. package/dist/configuration-BVMf6Q_j.js.map +0 -1
  68. package/dist/declarations.js.map +0 -1
  69. package/dist/doctor.js.map +0 -1
  70. package/dist/loader-compiler-D1teTrXw.js.map +0 -1
  71. package/dist/project.js.map +0 -1
  72. package/dist/vite.js.map +0 -1
@@ -3,7 +3,7 @@ import { Parser, tokTypes } from "acorn";
3
3
  import { tsPlugin } from "acorn-typescript";
4
4
  import jsx from "acorn-jsx";
5
5
  import { GREATEST_LOWER_BOUND, TraceMap, decodedMappings, encodedMappings, generatedPositionFor, originalPositionFor } from "@jridgewell/trace-mapping";
6
- //#region src/parser.js
6
+ // src/parser.js
7
7
  var options = {
8
8
  ecmaVersion: "latest",
9
9
  sourceType: "module",
@@ -19,9 +19,12 @@ function guardAt(input, offset, implicitMember = false) {
19
19
  const lexer = Parser.tokenizer(input.slice(offset), { ecmaVersion: "latest" });
20
20
  const stack = [];
21
21
  let first = true;
22
+ let missingElse = false;
23
+ let afterNonNull = false;
22
24
  try {
23
25
  for (;;) {
24
- const label = lexer.getToken().type.label;
26
+ const token = lexer.getToken();
27
+ const label = token.type.label;
25
28
  if (first && [
26
29
  "=",
27
30
  ".",
@@ -35,6 +38,22 @@ function guardAt(input, offset, implicitMember = false) {
35
38
  "in",
36
39
  "instanceof"
37
40
  ].includes(label) && !(implicitMember && label === ".")) return false;
41
+ if (first || afterNonNull) {
42
+ missingElse = !/[\r\n\u2028\u2029]/.test(input.slice(offset, offset + token.start)) && (token.type === tokTypes.name && !["as", "satisfies"].includes(token.value) || [
43
+ "const",
44
+ "num",
45
+ "string",
46
+ "true",
47
+ "false",
48
+ "null",
49
+ "this",
50
+ "new",
51
+ "typeof",
52
+ "void",
53
+ "delete"
54
+ ].includes(label) || token.type === tokTypes.prefix && token.value === "~" || implicitMember && first && label === ".");
55
+ afterNonNull = token.type === tokTypes.prefix && token.value === "!";
56
+ }
38
57
  if (!stack.length && [
39
58
  "if",
40
59
  "for",
@@ -45,15 +64,12 @@ function guardAt(input, offset, implicitMember = false) {
45
64
  "throw",
46
65
  "var",
47
66
  "export"
48
- ].includes(label)) return false;
49
- if (!first && !stack.length && label === "const") return false;
67
+ ].includes(label)) return missingElse ? "missingElse" : false;
68
+ if (!first && !stack.length && label === "const") return missingElse ? "missingElse" : false;
50
69
  first = false;
51
70
  if (!stack.length && label === "else") return true;
52
- if (!stack.length && [
53
- "eof",
54
- ";",
55
- "}"
56
- ].includes(label)) return false;
71
+ if (label === "eof") return !stack.length && missingElse ? "missingElse" : false;
72
+ if (!stack.length && [";", "}"].includes(label)) return missingElse ? "missingElse" : false;
57
73
  if ([
58
74
  "(",
59
75
  "[",
@@ -290,7 +306,9 @@ function parserFor(language) {
290
306
  node.cleanup = cleanup;
291
307
  return this.finishNode(node, "DeferStatement");
292
308
  }
293
- if (!this.isContextual("guard") || !guardAt(this.input, this.end, !!this.implicitClosure)) {
309
+ const guard = this.isContextual("guard") && guardAt(this.input, this.end, !!this.implicitClosure);
310
+ if (guard === "missingElse") this.raise(this.start, "Missing 'else' in guard statement. Put 'else' before the failure block: 'guard condition else { return ...; }'.");
311
+ if (!guard) {
294
312
  const statement = super.parseStatement(context, ...args);
295
313
  if (context && statement.type === "TwillEnumDeclaration") this.raise(statement.start, "An associated-value enum requires a block; add braces around this body.");
296
314
  return statement;
@@ -299,6 +317,7 @@ function parserFor(language) {
299
317
  node.singleStatement = !!context;
300
318
  this.next();
301
319
  node.binding = null;
320
+ if (this.type === tokTypes._else) this.raise(this.start, "Guard needs a condition or 'const' binding before 'else'.");
302
321
  if (this.type === tokTypes._const) {
303
322
  if (context) this.raise(node.start, "A guard binding requires a block; add braces around this body.");
304
323
  const declaration = this.startNode();
@@ -308,8 +327,9 @@ function parserFor(language) {
308
327
  if (node.binding.declarations.length !== 1) this.raise(node.start, "guard const requires one binding declaration.");
309
328
  } else node.test = this.parseExpression();
310
329
  node.elseStart = this.start;
311
- this.expect(tokTypes._else);
312
- if (this.type !== tokTypes.braceL) this.unexpected();
330
+ if (this.type !== tokTypes._else) this.raise(this.start, "Expected 'else' after the guard condition or binding.");
331
+ this.next();
332
+ if (this.type !== tokTypes.braceL) this.raise(this.start, "Guard 'else' must be followed by a block, for example 'else { return ...; }'.");
313
333
  node.failure = this.parseBlock();
314
334
  if (!exits(node.failure)) this.raise(node.failure.start, "Every guard else path must exit with return, throw, break or continue.");
315
335
  return this.finishNode(node, "GuardStatement");
@@ -694,13 +714,11 @@ function parse(source, language = "ts", sourceType = "module") {
694
714
  }))
695
715
  };
696
716
  }
697
- //#endregion
698
- //#region src/defer-helper.ts
717
+ // src/defer-helper.ts
699
718
  function syncDeferBody(names) {
700
719
  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";
701
720
  }
702
- //#endregion
703
- //#region src/defer.ts
721
+ // src/defer.ts
704
722
  var functions = /* @__PURE__ */ new Set([
705
723
  "FunctionDeclaration",
706
724
  "FunctionExpression",
@@ -876,8 +894,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
876
894
  code.prependLeft(start, `\nimport { runDefers as ${sharedHelper} } from '@swiftuijs/twill-runtime/helpers/v1';\n`);
877
895
  }
878
896
  }
879
- //#endregion
880
- //#region src/branches.ts
897
+ // src/branches.ts
881
898
  function suspension(node) {
882
899
  if (["AwaitExpression", "YieldExpression"].includes(node.type)) return node;
883
900
  if ([
@@ -897,10 +914,53 @@ function suspension(node) {
897
914
  }
898
915
  }
899
916
  }
917
+ function directEval(node) {
918
+ if ([
919
+ "FunctionDeclaration",
920
+ "FunctionExpression",
921
+ "ArrowFunctionExpression"
922
+ ].includes(node.type)) return false;
923
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
924
+ return Object.entries(node).some(([key, value]) => ![
925
+ "loc",
926
+ "trailing",
927
+ "tag"
928
+ ].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
929
+ }
930
+ /** Preserve the existing expression boundary for direct eval and suspension. */
931
+ function canInlineSwitchExpression(node) {
932
+ return !directEval(node) && !suspension(node);
933
+ }
934
+ /** Reuse authored blocks only when no direct eval (including a captured one)
935
+ * can observe the added temporary's broader lifetime/temporal dead zone.
936
+ * Memoize shared parser nodes so this analysis stays linear in the AST size. */
937
+ function reusableStatements(ast, usedNames) {
938
+ const reusable = /* @__PURE__ */ new Set();
939
+ const observed = /* @__PURE__ */ new Map();
940
+ const visit = (node) => {
941
+ const known = observed.get(node);
942
+ if (known !== void 0) return known;
943
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
944
+ let observable = node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval";
945
+ for (const [key, value] of Object.entries(node)) {
946
+ if (["loc", "trailing"].includes(key)) continue;
947
+ for (const child of Array.isArray(value) ? value : [value]) if (child?.type && visit(child)) observable = true;
948
+ }
949
+ if (!observable && node.type === "BlockStatement") {
950
+ for (const statement of node.body) if (statement.type === "TwillIfBinding" || (statement.argument ?? statement.expression)?.type === "TwillSwitchExpression") reusable.add(statement);
951
+ }
952
+ observed.set(node, observable);
953
+ return observable;
954
+ };
955
+ visit(ast);
956
+ return reusable;
957
+ }
900
958
  /** Native switches with per-arm scopes. Direct returns and standalone variable
901
959
  * initializers need no extra function; other expressions keep a lexical IIFE. */
902
- function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail) {
960
+ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail, implicitReturns = [], reusable = /* @__PURE__ */ new Set()) {
903
961
  const returns = /* @__PURE__ */ new Set();
962
+ const implicit = new Set(implicitReturns);
963
+ const sharedScopes = /* @__PURE__ */ new Set();
904
964
  const initializers = /* @__PURE__ */ new Map();
905
965
  const leadingDoc = (start) => {
906
966
  let low = 0, high = comments.length;
@@ -912,19 +972,6 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
912
972
  const comment = comments[low - 1];
913
973
  return comment && /^\s*$/.test(source.slice(comment.end, start)) ? comment : void 0;
914
974
  };
915
- const directEval = (node) => {
916
- if ([
917
- "FunctionDeclaration",
918
- "FunctionExpression",
919
- "ArrowFunctionExpression"
920
- ].includes(node.type)) return false;
921
- if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
922
- return Object.entries(node).some(([key, value]) => ![
923
- "loc",
924
- "trailing",
925
- "tag"
926
- ].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
927
- };
928
975
  const grouping = (start, end, initializer = false) => {
929
976
  const parentheses = [];
930
977
  const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
@@ -937,6 +984,15 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
937
984
  for (const token of parentheses) code.remove(start + token.start, start + token.end);
938
985
  };
939
986
  const visit = (node, parent) => {
987
+ if (node.type === "BlockStatement") for (const statement of node.body) {
988
+ const value = statement.type === "ReturnStatement" ? statement.argument : statement.expression;
989
+ if (value?.type === "TwillSwitchExpression" && (node.body.length === 1 || reusable.has(statement)) && (statement.type === "ReturnStatement" || implicit.has(value))) sharedScopes.add(value);
990
+ }
991
+ if (node.type === "ExpressionStatement" && implicit.has(node.expression)) {
992
+ grouping(node.start, node.expression.start);
993
+ grouping(node.expression.end, node.end);
994
+ if (source[node.end - 1] === ";") code.remove(node.end - 1, node.end);
995
+ }
940
996
  if (node.type === "VariableDeclaration" && node.declarations.length === 1) {
941
997
  const declaration = node.declarations[0];
942
998
  if (declaration.id.type === "Identifier" && declaration.init?.type === "TwillSwitchExpression" && [
@@ -958,6 +1014,7 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
958
1014
  if (node.type === "ReturnStatement" && node.argument?.type === "TwillSwitchExpression") {
959
1015
  returns.add(node.argument);
960
1016
  code.remove(node.start, node.start + 6);
1017
+ if (source[node.end - 1] === ";") code.remove(node.end - 1, node.end);
961
1018
  for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) grouping(start, end);
962
1019
  }
963
1020
  for (const [key, value] of Object.entries(node)) {
@@ -980,8 +1037,8 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
980
1037
  return name;
981
1038
  };
982
1039
  for (const node of switches) {
983
- const direct = returns.has(node);
984
- const suspended = !direct && suspension(node);
1040
+ const direct = returns.has(node) || implicit.has(node);
1041
+ const suspended = !returns.has(node) && suspension(node);
985
1042
  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.`);
986
1043
  const initializer = initializers.get(node);
987
1044
  const result = initializer ? fresh("Result") : void 0;
@@ -1000,7 +1057,10 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
1000
1057
  const valuePrefix = result ? `${result} = (` : "return ";
1001
1058
  const valueSuffix = result ? `); break ${exit};` : ";";
1002
1059
  const kind = node.cases.some((branch) => branch.enumPattern) ? fresh("Kind") : void 0;
1003
- if (!initializer) code.appendLeft(node.start, `${direct ? "{" : "(() => {"}const ${subject} = `);
1060
+ if (!initializer) {
1061
+ const opening = direct ? sharedScopes.has(node) ? "" : "{" : "(() => {";
1062
+ code.appendLeft(node.start, `${opening}const ${subject} = `);
1063
+ }
1004
1064
  code.remove(node.parenStart, node.parenStart + 1);
1005
1065
  if (node.keyword === "match") {
1006
1066
  const tokens = Parser.tokenizer(source.slice(node.discriminant.end, node.parenEnd), { ecmaVersion: "latest" });
@@ -1057,13 +1117,13 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
1057
1117
  }
1058
1118
  }
1059
1119
  const hasDefault = node.cases.some((branch) => !branch.pattern && !branch.enumPattern && branch.test === null);
1060
- const check = !hasDefault && language.startsWith("ts") ? ` ${kind ? `(0 as unknown as typeof ${kind})` : subject} satisfies never;` : "";
1061
- const failure = hasDefault ? "" : `${check} throw new TypeError('Non-exhaustive switch expression');`;
1062
- code.overwrite(node.end - 1, node.end, `}${failure}${direct || initializer ? "}" : "})()"}`, { contentOnly: true });
1120
+ const check = !hasDefault && language.startsWith("ts") ? fresh("Check") : void 0;
1121
+ const failure = hasDefault ? "" : `${check ? ` type ${check}<T extends never = typeof ${kind ?? subject}> = T & never;` : ""} throw new TypeError('Non-exhaustive switch expression'${check ? ` as ${check}` : ""});`;
1122
+ const closing = initializer || direct && !sharedScopes.has(node) ? "}" : direct ? "" : "})()";
1123
+ code.overwrite(node.end - 1, node.end, `}${failure}${closing}`, { contentOnly: true });
1063
1124
  }
1064
1125
  }
1065
- //#endregion
1066
- //#region src/enums.ts
1126
+ // src/enums.ts
1067
1127
  /** Find free type references, respecting generic function/mapped/infer binders. */
1068
1128
  function references(node, names, found = /* @__PURE__ */ new Set(), bound = /* @__PURE__ */ new Set()) {
1069
1129
  const local = new Set(bound);
@@ -1242,8 +1302,7 @@ function mapEnumCopies(source, generated, map, insertions) {
1242
1302
  }));
1243
1303
  return copies;
1244
1304
  }
1245
- //#endregion
1246
- //#region src/compiler.ts
1305
+ // src/compiler.ts
1247
1306
  function inferLanguage(filename) {
1248
1307
  if (/\.(?:twillx|tsx)$/.test(filename)) return "tsx";
1249
1308
  if (/\.jsx$/.test(filename)) return "jsx";
@@ -1268,6 +1327,25 @@ var TwillSyntaxError = class extends SyntaxError {
1268
1327
  this.frame = `${source.split(/\r\n|[\r\n\u2028\u2029]/)[line - 1] ?? ""}\n${" ".repeat(column)}^`;
1269
1328
  }
1270
1329
  };
1330
+ function closureExpression(body) {
1331
+ if (body.length !== 1) return;
1332
+ return body[0].type === "ReturnStatement" ? body[0].argument : body[0].expression;
1333
+ }
1334
+ function conciseExpression(body) {
1335
+ const expression = closureExpression(body);
1336
+ return !!expression && (expression.type !== "TwillSwitchExpression" || !canInlineSwitchExpression(expression));
1337
+ }
1338
+ function expressionGrouping(expression, source, start) {
1339
+ while ([
1340
+ "TSAsExpression",
1341
+ "TSSatisfiesExpression",
1342
+ "TSNonNullExpression"
1343
+ ].includes(expression.type)) expression = expression.expression;
1344
+ if (!["ObjectExpression", "SequenceExpression"].includes(expression.type)) return "";
1345
+ const tokens = Parser.tokenizer(source.slice(start, expression.start), { ecmaVersion: "latest" });
1346
+ for (let token = tokens.getToken(); token.type !== tokTypes.eof; token = tokens.getToken()) if (token.type === tokTypes.parenL) return "";
1347
+ return "(";
1348
+ }
1271
1349
  function walk(node, visit) {
1272
1350
  visit(node);
1273
1351
  for (const key of Object.keys(node)) {
@@ -1297,7 +1375,9 @@ function transform(source, options = {}) {
1297
1375
  const code = new MagicString(source);
1298
1376
  const enumInsertions = parsed.enums.length ? lowerEnums(source, parsed.ast, parsed.enums, code) : [];
1299
1377
  const usedNames = /* @__PURE__ */ new Set();
1300
- 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) => {
1378
+ const reusable = parsed.ifBindings.length || parsed.switches.length ? reusableStatements(parsed.ast, usedNames) : /* @__PURE__ */ new Set();
1379
+ const patternGuards = parsed.guards.filter((guard) => guard.binding && guard.binding.declarations[0].id.type !== "Identifier");
1380
+ if (!parsed.ifBindings.length && !parsed.switches.length && (patternGuards.length || parsed.defers.length || parsed.closures.some((closure) => closure.implicitMembers.length) || /\.twillx$/.test(filename.split(/[?#]/, 1)[0]))) walk(parsed.ast, (node) => {
1301
1381
  if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
1302
1382
  });
1303
1383
  let guardCounter = 0;
@@ -1310,12 +1390,13 @@ function transform(source, options = {}) {
1310
1390
  while (usedNames.has(temporary));
1311
1391
  usedNames.add(temporary);
1312
1392
  const patternEnd = binding.typeAnnotation?.start ?? binding.end;
1313
- code.overwrite(statement.start, binding.start, `{const ${temporary}`);
1393
+ const reuseScope = reusable.has(statement);
1394
+ code.overwrite(statement.start, binding.start, `${reuseScope ? "" : "{"}const ${temporary}`);
1314
1395
  code.move(binding.start, patternEnd, statement.consequent.start + 1);
1315
1396
  code.prependRight(binding.start, "const ");
1316
1397
  code.appendLeft(patternEnd, ` = ${temporary};`);
1317
1398
  code.prependLeft(statement.consequent.start, `; if (${temporary} !== null && ${temporary} !== void 0) `);
1318
- code.appendLeft(statement.end, "}");
1399
+ if (!reuseScope) code.appendLeft(statement.end, "}");
1319
1400
  }
1320
1401
  for (const guard of parsed.guards) {
1321
1402
  if (guard.singleStatement) {
@@ -1324,22 +1405,24 @@ function transform(source, options = {}) {
1324
1405
  }
1325
1406
  if (guard.binding) {
1326
1407
  const binding = guard.binding.declarations[0].id;
1327
- if (binding.type === "Identifier") code.remove(guard.start, guard.start + 5);
1328
- else {
1408
+ let name;
1409
+ if (binding.type === "Identifier") {
1410
+ name = binding.name;
1411
+ code.remove(guard.start, guard.start + 5);
1412
+ } else {
1329
1413
  let temporary;
1330
1414
  do
1331
1415
  temporary = `__twillGuard${guardCounter++}`;
1332
1416
  while (usedNames.has(temporary));
1333
1417
  usedNames.add(temporary);
1418
+ name = temporary;
1334
1419
  const patternEnd = binding.typeAnnotation?.start ?? binding.end;
1335
1420
  code.overwrite(guard.start, binding.start, `const ${temporary}`);
1336
1421
  code.move(binding.start, patternEnd, guard.end);
1337
1422
  code.prependRight(binding.start, "; const ");
1338
1423
  code.appendLeft(patternEnd, ` = ${temporary};`);
1339
- code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${temporary} == null)`);
1340
- continue;
1341
1424
  }
1342
- code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${binding.name} == null)`);
1425
+ code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${name} === null || ${name} === void 0)`);
1343
1426
  } else {
1344
1427
  code.overwrite(guard.start, guard.start + 5, "if (!(");
1345
1428
  code.overwrite(guard.elseStart, guard.elseStart + 4, "))");
@@ -1370,7 +1453,9 @@ function transform(source, options = {}) {
1370
1453
  name = `__twillArg${implicitCounter++}`;
1371
1454
  while (usedNames.has(name));
1372
1455
  usedNames.add(name);
1373
- code.overwrite(metadata.node.start, metadata.node.start + 1, `(${name}) => {`);
1456
+ const body = metadata.node.body.body;
1457
+ const opening = (options.implicitReturn !== false || body[0]?.type === "ReturnStatement") && conciseExpression(body) ? expressionGrouping(closureExpression(body), source, metadata.header?.end ?? metadata.node.start + 1) : "{";
1458
+ code.overwrite(metadata.node.start, metadata.node.start + 1, `(${name}) => ${opening}`);
1374
1459
  for (const offset of metadata.implicitMembers) code.appendLeft(offset, name);
1375
1460
  }
1376
1461
  const pragma = [...parsed.comments.filter((comment) => comment.start < (parsed.ast.body[0]?.start ?? source.length)).map((comment) => comment.value).join("\n").matchAll(/@jsxImportSource\s+(\S+)/g)].at(-1)?.[1];
@@ -1398,9 +1483,13 @@ function transform(source, options = {}) {
1398
1483
  return edits.toString();
1399
1484
  };
1400
1485
  let counter = 0;
1486
+ const directReturns = [];
1401
1487
  for (const node of parsed.calls) {
1402
1488
  const { callEnd, hadParens, originalArgs, closures } = node.trailing;
1403
1489
  const component = componentCalls.has(node);
1490
+ const slotObject = vue && (options.vueDefaultSlot !== "function" || closures.length > 1);
1491
+ let nativeChild = false;
1492
+ let bareChild = false;
1404
1493
  if (component) {
1405
1494
  if (originalArgs.length > 1 || originalArgs[0]?.type === "SpreadElement" || node.optional || node.callee.optional) throw new TwillSyntaxError(source, filename, {
1406
1495
  message: "Component syntax accepts one props object and children; optional calls use ordinary callbacks.",
@@ -1425,8 +1514,13 @@ function transform(source, options = {}) {
1425
1514
  labels.add(name);
1426
1515
  }
1427
1516
  }
1428
- const opening = vue ? ">{({default: " : ">{(";
1429
1517
  const tagEnd = node.typeParameters?.end ?? node.callee.end;
1518
+ const child = closures[0];
1519
+ const expression = child.body.body[0]?.expression;
1520
+ nativeChild = !vue && !metadataByNode.get(child).header && child.body.body.length === 1 && (expression?.type === "JSXElement" || expression?.type === "JSXFragment") && source.slice(child.start + 1, expression.start).trim() === "" && /^\s*;?\s*$/.test(source.slice(expression.end, child.end - 1)) && source.slice(callEnd, child.start).trim() === "" && (!hadParens || originalArgs.length > 0 || /^\s*\(\s*\)$/.test(source.slice(tagEnd, callEnd)));
1521
+ bareChild = !vue && (!!metadataByNode.get(child).header || child.body.body.length === 1 && expression !== void 0 && expression.type !== "SequenceExpression");
1522
+ let opening = nativeChild ? ">" : bareChild ? ">{" : ">{(";
1523
+ if (vue) opening = slotObject ? ">{{default: " : ">{";
1430
1524
  code.appendLeft(node.callee.start, "(<");
1431
1525
  const props = originalArgs[0];
1432
1526
  if (!vue && props?.type === "ObjectExpression" && props.properties.every((property) => property.type === "SpreadElement" || property.kind === "init" && !property.method && !property.computed && (property.key.name ?? property.key.value) !== "__proto__" && /^[A-Za-z_$][\w$.:-]*$/.test(String(property.key.name ?? property.key.value)))) {
@@ -1473,7 +1567,7 @@ function transform(source, options = {}) {
1473
1567
  const tokens = Parser.tokenizer(gap, { ecmaVersion: "latest" });
1474
1568
  const first = tokens.getToken();
1475
1569
  const last = tokens.getToken();
1476
- code.overwrite(tagEnd, callEnd, opening + gap.slice(first.end, last.start));
1570
+ code.overwrite(tagEnd, callEnd, opening + (nativeChild ? "" : gap.slice(first.end, last.start)));
1477
1571
  } else code.appendLeft(callEnd, opening);
1478
1572
  } else if (hadParens) {
1479
1573
  const tailStart = originalArgs.at(-1)?.end ?? callEnd - 1;
@@ -1496,24 +1590,40 @@ function transform(source, options = {}) {
1496
1590
  "DeferStatement",
1497
1591
  "EmptyStatement"
1498
1592
  ].includes(statement.type)) && body.filter((statement) => statement.type !== "EmptyStatement").at(-1) === expressions[0];
1593
+ const explicitReturn = !content && body.length === 1 && body[0].type === "ReturnStatement" && body[0].argument;
1594
+ const returnsValue = explicitReturn || singleContent || options.implicitReturn !== false && body.length === 1 && body[0].type === "ExpressionStatement";
1595
+ const conciseBody = returnsValue && conciseExpression(body);
1596
+ const grouping = conciseBody ? expressionGrouping(closureExpression(body), source, metadata.header?.end ?? closure.start + 1) : "{";
1499
1597
  if (singleContent && body.length === 1 && !vue) {
1500
1598
  directChild = true;
1501
- code.remove(closure.start, closure.start + 1);
1502
- code.remove(closure.end - 1, closure.end);
1503
1599
  const statement = expressions[0];
1504
- if (source[statement.end - 1] === ";") code.remove(statement.end - 1, statement.end);
1600
+ if (nativeChild) {
1601
+ code.remove(callEnd, statement.expression.start);
1602
+ code.overwrite(statement.expression.end, closure.end, "", { contentOnly: true });
1603
+ } else {
1604
+ code.remove(closure.start, closure.start + 1);
1605
+ code.overwrite(closure.end - 1, closure.end, "", { contentOnly: true });
1606
+ if (source[statement.end - 1] === ";") code.remove(statement.end - 1, statement.end);
1607
+ }
1505
1608
  continue;
1506
1609
  }
1507
1610
  if (metadata.label) {
1508
1611
  const label = source.slice(metadata.label.start, metadata.label.end).replace(/\s*:\s*$/, "");
1509
- code.overwrite(metadata.label.start, closure.start, component && vue ? `, [${JSON.stringify(label)}]: ` : ", ");
1612
+ code.overwrite(metadata.label.start, closure.start, component && vue ? `, ${label === "__proto__" ? `[${JSON.stringify(label)}]` : JSON.stringify(label)}: ` : ", ");
1510
1613
  }
1511
1614
  if (metadata.header) {
1512
1615
  const header = metadata.header;
1513
1616
  code.overwrite(closure.start, closure.start + 1, header.parenthesized || metadata.async ? "" : "(");
1514
1617
  if (metadata.async && !header.parenthesized) code.appendLeft(header.asyncEnd, " (");
1515
- code.overwrite(header.inStart, header.end, `${header.parenthesized ? "" : ") "}=> {`);
1516
- } else if (!metadata.implicitMembers.length) code.overwrite(closure.start, closure.start + 1, "() => {");
1618
+ code.overwrite(header.inStart, header.end, `${header.parenthesized ? "" : ") "}=> ${grouping}`);
1619
+ } else if (!metadata.implicitMembers.length) code.overwrite(closure.start, closure.start + 1, `() => ${grouping}`);
1620
+ if (conciseBody) {
1621
+ const statement = body[0];
1622
+ if (explicitReturn) code.remove(statement.start, statement.start + 6);
1623
+ if (source[statement.end - 1] === ";") code.remove(statement.end - 1, statement.end);
1624
+ code.overwrite(closure.end - 1, closure.end, grouping ? ")" : "", { contentOnly: true });
1625
+ continue;
1626
+ }
1517
1627
  if (content && !singleContent) {
1518
1628
  let collector;
1519
1629
  do
@@ -1566,13 +1676,16 @@ function transform(source, options = {}) {
1566
1676
  body.forEach(collect);
1567
1677
  code.prependLeft(bodyStart, `const ${collector} = [];`);
1568
1678
  code.prependLeft(closure.end - 1, vue ? `;return ${collector};` : `;return ${collector}.length === 1 ? ${collector}[0] : ${collector};`);
1569
- } else if (singleContent || options.implicitReturn !== false && body.length === 1 && body[0].type === "ExpressionStatement") {
1679
+ } else if (returnsValue && !explicitReturn) {
1570
1680
  const statement = singleContent ? expressions[0] : body[0];
1571
- code.prependLeft(statement.start, "return (");
1572
- code.appendLeft(source[statement.end - 1] === ";" ? statement.end - 1 : statement.end, ")");
1681
+ if (statement.expression.type === "TwillSwitchExpression" && canInlineSwitchExpression(statement.expression)) directReturns.push(statement.expression);
1682
+ else {
1683
+ code.prependLeft(statement.start, "return (");
1684
+ code.appendLeft(source[statement.end - 1] === ";" ? statement.end - 1 : statement.end, ")");
1685
+ }
1573
1686
  }
1574
1687
  }
1575
- if (component) code.prependLeft(closures.at(-1).end, (vue ? "})}" : directChild || metadataByNode.get(closures[0]).header ? ")}" : ")()}") + `</${calleeName(node.callee)}>)`);
1688
+ if (component) code.prependLeft(closures.at(-1).end, (vue ? slotObject ? "}}" : "}" : nativeChild ? "" : bareChild ? "}" : directChild ? ")}" : ")()}") + `</${calleeName(node.callee)}>)`);
1576
1689
  else code.appendLeft(closures.at(-1).end, ")");
1577
1690
  }
1578
1691
  if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, parsed.comments, (node, message) => {
@@ -1581,7 +1694,7 @@ function transform(source, options = {}) {
1581
1694
  pos: node.start,
1582
1695
  loc: node.loc.start
1583
1696
  });
1584
- });
1697
+ }, directReturns, reusable);
1585
1698
  for (const end of attributeClosings) code.appendLeft(end, "}");
1586
1699
  if (parsed.defers.length) {
1587
1700
  const starts = /* @__PURE__ */ new Map();
@@ -1640,7 +1753,6 @@ function generatedPosition(result, line, column) {
1640
1753
  bias: GREATEST_LOWER_BOUND
1641
1754
  });
1642
1755
  }
1643
- //#endregion
1644
1756
  export { transform as a, originalPosition as i, generatedPosition as n, parse as o, inferLanguage as r, TwillSyntaxError as t };
1645
1757
 
1646
- //# sourceMappingURL=compiler-CbNSBUI2.js.map
1758
+ //# sourceMappingURL=compiler-DI0H4d1R.js.map