@swiftuijs/twill 0.3.1 → 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 (58) hide show
  1. package/README.md +8 -3
  2. package/dist/branches.d.ts +7 -1
  3. package/dist/cli.js +7 -8
  4. package/dist/{compilation-cache-7SugqJ4n.js → compilation-cache-BrV71YKt.js} +2 -3
  5. package/dist/{compilation-cache-7SugqJ4n.js.map → compilation-cache-BrV71YKt.js.map} +1 -1
  6. package/dist/{compiler-BdkU4Ruh.js → compiler-DI0H4d1R.js} +147 -55
  7. package/dist/compiler-DI0H4d1R.js.map +1 -0
  8. package/dist/compiler.d.ts +2 -0
  9. package/dist/config.js +1 -2
  10. package/dist/{configuration-BVMf6Q_j.js → configuration-ppqhmuy4.js} +7 -5
  11. package/dist/configuration-ppqhmuy4.js.map +1 -0
  12. package/dist/configuration.d.ts +1 -1
  13. package/dist/{declarations-BTAS8Mb_.js → declarations-DXtb1AHP.js} +3 -4
  14. package/dist/{declarations-BTAS8Mb_.js.map → declarations-DXtb1AHP.js.map} +1 -1
  15. package/dist/declarations.js +1 -2
  16. package/dist/{doctor-Co_pdZtt.js → doctor-BHwU_YBw.js} +4 -6
  17. package/dist/{doctor-Co_pdZtt.js.map → doctor-BHwU_YBw.js.map} +1 -1
  18. package/dist/doctor.js +1 -2
  19. package/dist/editor.d.ts +1 -0
  20. package/dist/editor.js +31 -5
  21. package/dist/editor.js.map +1 -1
  22. package/dist/esbuild.js +2 -3
  23. package/dist/{extensions-B1xTkoYP.js → extensions-7ixttcoK.js} +2 -3
  24. package/dist/{extensions-B1xTkoYP.js.map → extensions-7ixttcoK.js.map} +1 -1
  25. package/dist/{files-BW9geU0_.js → files-CgwJJ992.js} +3 -4
  26. package/dist/{files-BW9geU0_.js.map → files-CgwJJ992.js.map} +1 -1
  27. package/dist/index.js +2 -2
  28. package/dist/loader-compiler.js +3 -4
  29. package/dist/{loader-registration-B1ST9h1e.js → loader-registration-BO43rvVU.js} +2 -3
  30. package/dist/{loader-registration-B1ST9h1e.js.map → loader-registration-BO43rvVU.js.map} +1 -1
  31. package/dist/loader-registration.js +1 -2
  32. package/dist/{loader-state-DE2ArGYl.js → loader-state-DB7sONR_.js} +5 -6
  33. package/dist/{loader-state-DE2ArGYl.js.map → loader-state-DB7sONR_.js.map} +1 -1
  34. package/dist/loader-sync.js +2 -3
  35. package/dist/loader.js +2 -3
  36. package/dist/{plugin-DdTVagIU.js → plugin-DDOHhv7Z.js} +6 -7
  37. package/dist/{plugin-DdTVagIU.js.map → plugin-DDOHhv7Z.js.map} +1 -1
  38. package/dist/{project-ZmlpFooa.js → project-DYXf2Kgt.js} +7 -9
  39. package/dist/{project-ZmlpFooa.js.map → project-DYXf2Kgt.js.map} +1 -1
  40. package/dist/project.js +2 -2
  41. package/dist/register.js +2 -3
  42. package/dist/rollup.js +2 -3
  43. package/dist/rspack.js +2 -3
  44. package/dist/syntax.js +2 -3
  45. package/dist/{transpile-CLNavDUr.js → transpile--6CFGUm0.js} +3 -4
  46. package/dist/{transpile-CLNavDUr.js.map → transpile--6CFGUm0.js.map} +1 -1
  47. package/dist/typescript-plugin.cjs +221 -117
  48. package/dist/typescript-plugin.cjs.map +1 -1
  49. package/dist/vite-D0Ct4uy4.js +6 -0
  50. package/dist/{vite-BI-5KRgr.js.map → vite-D0Ct4uy4.js.map} +1 -1
  51. package/dist/vite-react.js +2 -3
  52. package/dist/vite.js +1 -2
  53. package/dist/webpack.js +2 -3
  54. package/package.json +3 -3
  55. package/schemas/twill.schema.json +6 -0
  56. package/dist/compiler-BdkU4Ruh.js.map +0 -1
  57. package/dist/configuration-BVMf6Q_j.js.map +0 -1
  58. package/dist/vite-BI-5KRgr.js +0 -7
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # <img src="https://twill.evecalm.com/logo.png" alt="Twill hummingbird" align="right" width="40" height="40" /> Twill
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/%40swiftuijs%2Ftwill)](https://www.npmjs.com/package/@swiftuijs/twill)
4
+ [![npm monthly downloads](https://img.shields.io/npm/dm/%40swiftuijs%2Ftwill)](https://www.npmjs.com/package/@swiftuijs/twill)
5
+ [![CI](https://img.shields.io/github/actions/workflow/status/swiftuijs/twill/ci.yml?branch=main&label=CI)](https://github.com/swiftuijs/twill/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/npm/l/%40swiftuijs%2Ftwill)](https://github.com/swiftuijs/twill/blob/main/LICENSE)
7
+
3
8
  **TypeScript. Clearer flow.**
4
9
 
5
10
  Twill is a **TypeScript-based language with Swift-inspired syntax extensions**, built by [forth.ink](https://forth.ink). It extends TypeScript and TSX with guards, `defer`, trailing closures and checked switch expressions. Use TypeScript's type system and existing JavaScript libraries; compile to ordinary JavaScript.
@@ -73,8 +78,8 @@ The [playground](https://twill.evecalm.com/playground) runs the actual compiler
73
78
  Install the compiler as a development dependency in your application. Use Node 24 LTS for a new setup; the package supports `^20.19.0 || >=22.12.0`. Run from the directory containing your `package.json` (run `npm init -y` first for a new project). Default inline emission introduces no language runtime dependency into your application bundle. The [optional runtime](https://twill.evecalm.com/runtime) in 0.2.0 shares dynamic synchronous cleanup when explicitly selected.
74
79
 
75
80
  ```sh
76
- pnpm add -D @swiftuijs/twill@0.3.1
77
- # npm install --save-dev @swiftuijs/twill@0.3.1
81
+ pnpm add -D @swiftuijs/twill@0.4.0
82
+ # npm install --save-dev @swiftuijs/twill@0.4.0
78
83
  ```
79
84
 
80
85
  Add the Vite adapter to your existing configuration:
@@ -103,7 +108,7 @@ For a project-local command, install the compiler above, save this as `hello.twi
103
108
  console.log(process.argv.slice(2));
104
109
  ```
105
110
 
106
- Expected output is `[ 'hello world' ]`. On Linux/macOS, use `chmod +x hello.twill` then `npm exec --call './hello.twill "hello world"'` or `pnpm exec ./hello.twill 'hello world'` for a local shebang. For direct `./hello.twill` execution, install `@swiftuijs/twill` globally with `npm install --global @swiftuijs/twill@0.3.1` and keep `twill` on PATH. Windows uses the explicit CLI.
111
+ Expected output is `[ 'hello world' ]`. On Linux/macOS, use `chmod +x hello.twill` then `npm exec --call './hello.twill "hello world"'` or `pnpm exec ./hello.twill 'hello world'` for a local shebang. For direct `./hello.twill` execution, install `@swiftuijs/twill` globally with `npm install --global @swiftuijs/twill@0.4.0` and keep `twill` on PATH. Windows uses the explicit CLI.
107
112
 
108
113
  Install `@swiftuijs/twill-shell` in the script's project only when using its subprocess API; a global compiler does not install project imports. Follow [shell scripting](https://twill.evecalm.com/scripting) for complete installation, a portable subprocess example, Node type checking and troubleshooting. Native JS/TS consumers need only the SDK.
109
114
 
@@ -6,10 +6,16 @@ type Node = {
6
6
  end: number;
7
7
  [key: string]: any;
8
8
  };
9
+ /** Preserve the existing expression boundary for direct eval and suspension. */
10
+ export declare function canInlineSwitchExpression(node: Node): boolean;
11
+ /** Reuse authored blocks only when no direct eval (including a captured one)
12
+ * can observe the added temporary's broader lifetime/temporal dead zone.
13
+ * Memoize shared parser nodes so this analysis stays linear in the AST size. */
14
+ export declare function reusableStatements(ast: Node, usedNames: Set<string>): Set<Node>;
9
15
  /** Native switches with per-arm scopes. Direct returns and standalone variable
10
16
  * initializers need no extra function; other expressions keep a lexical IIFE. */
11
17
  export declare function lowerSwitchExpressions(source: string, ast: Node, switches: Node[], code: MagicString, language: Language, usedNames: Set<string>, comments: {
12
18
  start: number;
13
19
  end: number;
14
- }[], fail: (node: Node, message: string) => never): void;
20
+ }[], fail: (node: Node, message: string) => never, implicitReturns?: Node[], reusable?: Set<Node>): void;
15
21
  export {};
package/dist/cli.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { a as transform } from "./compiler-BdkU4Ruh.js";
3
- import { n as loadConfig } from "./configuration-BVMf6Q_j.js";
4
- import { t as TwillProject } from "./project-ZmlpFooa.js";
5
- import { t as inspectProject } from "./doctor-Co_pdZtt.js";
6
- import { t as emitDeclarations } from "./declarations-BTAS8Mb_.js";
7
- import { t as transpile } from "./transpile-CLNavDUr.js";
2
+ import { a as transform } from "./compiler-DI0H4d1R.js";
3
+ import { t as transpile } from "./transpile--6CFGUm0.js";
4
+ import { n as loadConfig } from "./configuration-ppqhmuy4.js";
5
+ import { t as TwillProject } from "./project-DYXf2Kgt.js";
6
+ import { t as inspectProject } from "./doctor-BHwU_YBw.js";
7
+ import { t as emitDeclarations } from "./declarations-DXtb1AHP.js";
8
8
  import { createRequire } from "node:module";
9
9
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
10
  import { dirname, resolve } from "node:path";
11
11
  import { pathToFileURL } from "node:url";
12
12
  import { parseArgs } from "node:util";
13
- //#region src/cli.ts
13
+ // src/cli.ts
14
14
  async function main(args = process.argv.slice(2)) {
15
15
  const { values, positionals, tokens } = parseArgs({
16
16
  args,
@@ -129,7 +129,6 @@ async function main(args = process.argv.slice(2)) {
129
129
  } else process.stdout.write(result.code + "\n");
130
130
  return 0;
131
131
  }
132
- //#endregion
133
132
  export { main };
134
133
 
135
134
  //# sourceMappingURL=cli.js.map
@@ -4,7 +4,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { createHash, randomUUID } from "node:crypto";
5
5
  import { homedir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
- //#region src/compilation-cache.ts
7
+ // src/compilation-cache.ts
8
8
  var limit = 524288;
9
9
  var packages = [
10
10
  "typescript",
@@ -166,7 +166,6 @@ function openCompilationCache() {
166
166
  if (identity) return new CompilationCache(resolve(directory), identity);
167
167
  } catch {}
168
168
  }
169
- //#endregion
170
169
  export { openCompilationCache as i, observationsMatch as n, observe as r, digest as t };
171
170
 
172
- //# sourceMappingURL=compilation-cache-7SugqJ4n.js.map
171
+ //# sourceMappingURL=compilation-cache-BrV71YKt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compilation-cache-7SugqJ4n.js","names":[],"sources":["../src/compilation-cache.ts"],"sourcesContent":["import {\n closeSync,\n constants,\n existsSync,\n fstatSync,\n lstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n realpathSync,\n statSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n type Stats,\n} from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { createRequire } from 'node:module';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport type ConfigurationObservation = {\n path: string;\n kind: 'file' | 'directory';\n value: string;\n};\nconst limit = 512 * 1024;\nconst packages = [\n 'typescript',\n 'acorn',\n 'acorn-typescript',\n 'acorn-jsx',\n 'magic-string',\n '@ampproject/remapping',\n '@jridgewell/trace-mapping',\n];\nexport const digest = (source: string | Uint8Array) =>\n createHash('sha256').update(source).digest('hex');\n\nexport function observe(path: string, kind: ConfigurationObservation['kind']): string {\n try {\n if (kind === 'directory') return statSync(path).isDirectory() ? 'directory' : 'absent';\n return digest(readFileSync(path));\n } catch (error) {\n return ['ENOENT', 'ENOTDIR'].includes((error as NodeJS.ErrnoException).code ?? '')\n ? 'absent'\n : 'unavailable';\n }\n}\nexport function observationsMatch(observations: ConfigurationObservation[]): boolean {\n return observations.every(({ path, kind, value }) => {\n const current = observe(path, kind);\n return current !== 'unavailable' && current === value;\n });\n}\n\n/** Hash actual installed code without evaluating any compiler or dependency module. */\nexport function toolchainIdentity(\n location = import.meta.url,\n names = packages,\n): string | undefined {\n try {\n const hash = createHash('sha256').update('twill-loader-cache-v1\\0' + process.version);\n const add = (path: string, bytes: Uint8Array) =>\n hash.update(JSON.stringify([path, digest(bytes)]));\n const directory = dirname(fileURLToPath(location));\n for (const file of readdirSync(directory).sort())\n if (file.endsWith('.js')) add(file, readFileSync(join(directory, file)));\n const seen = new Set<string>();\n const visit = (name: string, from: string) => {\n const require = createRequire(from);\n let root = dirname(require.resolve(name));\n for (;;) {\n const metadata = join(root, 'package.json');\n if (existsSync(metadata) && JSON.parse(readFileSync(metadata, 'utf8')).name === name) break;\n const parent = dirname(root);\n if (parent === root) throw new Error('Package metadata unavailable');\n root = parent;\n }\n root = realpathSync(root);\n if (seen.has(root)) return;\n seen.add(root);\n const metadata = join(root, 'package.json');\n const value = JSON.parse(readFileSync(metadata, 'utf8'));\n add(name, readFileSync(metadata));\n const walk = (folder: string) => {\n for (const entry of readdirSync(folder, { withFileTypes: true }).sort((a, b) =>\n a.name.localeCompare(b.name),\n )) {\n const path = join(folder, entry.name);\n if (entry.isSymbolicLink() && statSync(path).isDirectory())\n throw new Error('Directory symlink requires uncached compilation');\n if (entry.isDirectory() && entry.name !== 'node_modules') walk(path);\n else if (/\\.(?:[cm]?js|json)$/.test(entry.name))\n add(path.slice(root.length), readFileSync(path));\n }\n };\n // TypeScript emission/configuration executes this single compiler bundle;\n // hashing its CLI/server copies would add unrelated cold-start work.\n if (name === 'typescript')\n add('typescript/lib/typescript.js', readFileSync(require.resolve(name)));\n else walk(root);\n for (const dependency of Object.keys(value.dependencies ?? {}).sort())\n visit(dependency, metadata);\n };\n for (const name of names) visit(name, location);\n return hash.digest('hex');\n } catch {\n return undefined;\n }\n}\n\nfunction safeDirectory(directory: string): boolean {\n const info = lstatSync(directory);\n if (!info.isDirectory() || info.isSymbolicLink()) return false;\n if (process.platform === 'win32') return true; // User-selected/profile ACL, not a POSIX mode claim.\n const uid = process.getuid!();\n if (info.uid !== uid || (info.mode & 0o077) !== 0) return false;\n let path = realpathSync(directory);\n for (;;) {\n const parent = lstatSync(path);\n if (parent.uid !== uid && parent.uid !== 0) return false;\n if ((parent.mode & 0o022) !== 0 && (parent.mode & 0o1000) === 0) return false;\n const next = dirname(path);\n if (next === path) return true;\n path = next;\n }\n}\nfunction safeFile(info: Stats): boolean {\n return (\n info.isFile() &&\n info.nlink === 1 &&\n info.size <= limit &&\n (process.platform === 'win32' || (info.uid === process.getuid!() && (info.mode & 0o077) === 0))\n );\n}\n\n/** Fixed slots bound completed storage; collisions are misses, never shared module results. */\nexport class CompilationCache {\n constructor(\n private readonly directory: string,\n private readonly identity: string,\n ) {}\n\n private key(url: string, source: string, dialect: boolean) {\n return digest(JSON.stringify([this.identity, url, digest(source), dialect, 'ES2022']));\n }\n private slot(url: string) {\n return join(this.directory, (parseInt(digest(url).slice(0, 2), 16) % 128) + '.json');\n }\n read(url: string, source: string, dialect: boolean): string | undefined {\n let fd: number | undefined;\n try {\n if (!safeDirectory(this.directory)) return;\n const file = this.slot(url);\n if (lstatSync(file).isSymbolicLink()) return;\n fd = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\n if (!safeFile(fstatSync(fd))) return;\n const value = JSON.parse(readFileSync(fd, 'utf8'));\n if (\n value.key !== this.key(url, source, dialect) ||\n typeof value.source !== 'string' ||\n value.digest !== digest(value.source) ||\n !Array.isArray(value.observations) ||\n !value.observations.every(\n (entry: ConfigurationObservation) =>\n entry &&\n typeof entry.path === 'string' &&\n isAbsolute(entry.path) &&\n ['file', 'directory'].includes(entry.kind) &&\n typeof entry.value === 'string',\n ) ||\n !observationsMatch(value.observations)\n )\n return;\n return value.source;\n } catch {\n return;\n } finally {\n if (fd !== undefined) {\n try {\n closeSync(fd);\n } catch {\n /* Cache cleanup is optional too. */\n }\n }\n }\n }\n write(\n url: string,\n source: string,\n dialect: boolean,\n emitted: string,\n observations: ConfigurationObservation[],\n ): void {\n let temporary: string | undefined;\n try {\n const value = JSON.stringify({\n key: this.key(url, source, dialect),\n source: emitted,\n digest: digest(emitted),\n observations,\n });\n if (\n Buffer.byteLength(value) > limit ||\n !safeDirectory(this.directory) ||\n !observationsMatch(observations)\n )\n return;\n temporary = join(this.directory, randomUUID() + '.tmp');\n writeFileSync(temporary, value, { flag: 'wx', mode: 0o600 });\n renameSync(temporary, this.slot(url));\n } catch {\n // An executable cache is optional; its failure never changes script behavior.\n } finally {\n if (temporary !== undefined) {\n try {\n unlinkSync(temporary);\n } catch {\n // Renamed, concurrently removed, or inaccessible; never replace the program's result.\n }\n }\n }\n }\n}\n\nexport function openCompilationCache(): CompilationCache | undefined {\n try {\n const directory = process.env.TWILL_CACHE_DIR ?? join(homedir(), '.twill', 'script-cache-v1');\n if (!isAbsolute(directory)) return;\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n if (!safeDirectory(directory)) return;\n const identity = toolchainIdentity();\n if (identity) return new CompilationCache(resolve(directory), identity);\n } catch {\n // Permissions, unsupported environment or unavailable identity: compile normally.\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compilation-cache-BrV71YKt.js","names":[],"sources":["../src/compilation-cache.ts"],"sourcesContent":["import {\n closeSync,\n constants,\n existsSync,\n fstatSync,\n lstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n realpathSync,\n statSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n type Stats,\n} from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { createRequire } from 'node:module';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport type ConfigurationObservation = {\n path: string;\n kind: 'file' | 'directory';\n value: string;\n};\nconst limit = 512 * 1024;\nconst packages = [\n 'typescript',\n 'acorn',\n 'acorn-typescript',\n 'acorn-jsx',\n 'magic-string',\n '@ampproject/remapping',\n '@jridgewell/trace-mapping',\n];\nexport const digest = (source: string | Uint8Array) =>\n createHash('sha256').update(source).digest('hex');\n\nexport function observe(path: string, kind: ConfigurationObservation['kind']): string {\n try {\n if (kind === 'directory') return statSync(path).isDirectory() ? 'directory' : 'absent';\n return digest(readFileSync(path));\n } catch (error) {\n return ['ENOENT', 'ENOTDIR'].includes((error as NodeJS.ErrnoException).code ?? '')\n ? 'absent'\n : 'unavailable';\n }\n}\nexport function observationsMatch(observations: ConfigurationObservation[]): boolean {\n return observations.every(({ path, kind, value }) => {\n const current = observe(path, kind);\n return current !== 'unavailable' && current === value;\n });\n}\n\n/** Hash actual installed code without evaluating any compiler or dependency module. */\nexport function toolchainIdentity(\n location = import.meta.url,\n names = packages,\n): string | undefined {\n try {\n const hash = createHash('sha256').update('twill-loader-cache-v1\\0' + process.version);\n const add = (path: string, bytes: Uint8Array) =>\n hash.update(JSON.stringify([path, digest(bytes)]));\n const directory = dirname(fileURLToPath(location));\n for (const file of readdirSync(directory).sort())\n if (file.endsWith('.js')) add(file, readFileSync(join(directory, file)));\n const seen = new Set<string>();\n const visit = (name: string, from: string) => {\n const require = createRequire(from);\n let root = dirname(require.resolve(name));\n for (;;) {\n const metadata = join(root, 'package.json');\n if (existsSync(metadata) && JSON.parse(readFileSync(metadata, 'utf8')).name === name) break;\n const parent = dirname(root);\n if (parent === root) throw new Error('Package metadata unavailable');\n root = parent;\n }\n root = realpathSync(root);\n if (seen.has(root)) return;\n seen.add(root);\n const metadata = join(root, 'package.json');\n const value = JSON.parse(readFileSync(metadata, 'utf8'));\n add(name, readFileSync(metadata));\n const walk = (folder: string) => {\n for (const entry of readdirSync(folder, { withFileTypes: true }).sort((a, b) =>\n a.name.localeCompare(b.name),\n )) {\n const path = join(folder, entry.name);\n if (entry.isSymbolicLink() && statSync(path).isDirectory())\n throw new Error('Directory symlink requires uncached compilation');\n if (entry.isDirectory() && entry.name !== 'node_modules') walk(path);\n else if (/\\.(?:[cm]?js|json)$/.test(entry.name))\n add(path.slice(root.length), readFileSync(path));\n }\n };\n // TypeScript emission/configuration executes this single compiler bundle;\n // hashing its CLI/server copies would add unrelated cold-start work.\n if (name === 'typescript')\n add('typescript/lib/typescript.js', readFileSync(require.resolve(name)));\n else walk(root);\n for (const dependency of Object.keys(value.dependencies ?? {}).sort())\n visit(dependency, metadata);\n };\n for (const name of names) visit(name, location);\n return hash.digest('hex');\n } catch {\n return undefined;\n }\n}\n\nfunction safeDirectory(directory: string): boolean {\n const info = lstatSync(directory);\n if (!info.isDirectory() || info.isSymbolicLink()) return false;\n if (process.platform === 'win32') return true; // User-selected/profile ACL, not a POSIX mode claim.\n const uid = process.getuid!();\n if (info.uid !== uid || (info.mode & 0o077) !== 0) return false;\n let path = realpathSync(directory);\n for (;;) {\n const parent = lstatSync(path);\n if (parent.uid !== uid && parent.uid !== 0) return false;\n if ((parent.mode & 0o022) !== 0 && (parent.mode & 0o1000) === 0) return false;\n const next = dirname(path);\n if (next === path) return true;\n path = next;\n }\n}\nfunction safeFile(info: Stats): boolean {\n return (\n info.isFile() &&\n info.nlink === 1 &&\n info.size <= limit &&\n (process.platform === 'win32' || (info.uid === process.getuid!() && (info.mode & 0o077) === 0))\n );\n}\n\n/** Fixed slots bound completed storage; collisions are misses, never shared module results. */\nexport class CompilationCache {\n constructor(\n private readonly directory: string,\n private readonly identity: string,\n ) {}\n\n private key(url: string, source: string, dialect: boolean) {\n return digest(JSON.stringify([this.identity, url, digest(source), dialect, 'ES2022']));\n }\n private slot(url: string) {\n return join(this.directory, (parseInt(digest(url).slice(0, 2), 16) % 128) + '.json');\n }\n read(url: string, source: string, dialect: boolean): string | undefined {\n let fd: number | undefined;\n try {\n if (!safeDirectory(this.directory)) return;\n const file = this.slot(url);\n if (lstatSync(file).isSymbolicLink()) return;\n fd = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\n if (!safeFile(fstatSync(fd))) return;\n const value = JSON.parse(readFileSync(fd, 'utf8'));\n if (\n value.key !== this.key(url, source, dialect) ||\n typeof value.source !== 'string' ||\n value.digest !== digest(value.source) ||\n !Array.isArray(value.observations) ||\n !value.observations.every(\n (entry: ConfigurationObservation) =>\n entry &&\n typeof entry.path === 'string' &&\n isAbsolute(entry.path) &&\n ['file', 'directory'].includes(entry.kind) &&\n typeof entry.value === 'string',\n ) ||\n !observationsMatch(value.observations)\n )\n return;\n return value.source;\n } catch {\n return;\n } finally {\n if (fd !== undefined) {\n try {\n closeSync(fd);\n } catch {\n /* Cache cleanup is optional too. */\n }\n }\n }\n }\n write(\n url: string,\n source: string,\n dialect: boolean,\n emitted: string,\n observations: ConfigurationObservation[],\n ): void {\n let temporary: string | undefined;\n try {\n const value = JSON.stringify({\n key: this.key(url, source, dialect),\n source: emitted,\n digest: digest(emitted),\n observations,\n });\n if (\n Buffer.byteLength(value) > limit ||\n !safeDirectory(this.directory) ||\n !observationsMatch(observations)\n )\n return;\n temporary = join(this.directory, randomUUID() + '.tmp');\n writeFileSync(temporary, value, { flag: 'wx', mode: 0o600 });\n renameSync(temporary, this.slot(url));\n } catch {\n // An executable cache is optional; its failure never changes script behavior.\n } finally {\n if (temporary !== undefined) {\n try {\n unlinkSync(temporary);\n } catch {\n // Renamed, concurrently removed, or inaccessible; never replace the program's result.\n }\n }\n }\n }\n}\n\nexport function openCompilationCache(): CompilationCache | undefined {\n try {\n const directory = process.env.TWILL_CACHE_DIR ?? join(homedir(), '.twill', 'script-cache-v1');\n if (!isAbsolute(directory)) return;\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n if (!safeDirectory(directory)) return;\n const identity = toolchainIdentity();\n if (identity) return new CompilationCache(resolve(directory), identity);\n } catch {\n // Permissions, unsupported environment or unavailable identity: compile normally.\n }\n}\n"],"mappings":"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"}
@@ -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",
@@ -714,13 +714,11 @@ function parse(source, language = "ts", sourceType = "module") {
714
714
  }))
715
715
  };
716
716
  }
717
- //#endregion
718
- //#region src/defer-helper.ts
717
+ // src/defer-helper.ts
719
718
  function syncDeferBody(names) {
720
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";
721
720
  }
722
- //#endregion
723
- //#region src/defer.ts
721
+ // src/defer.ts
724
722
  var functions = /* @__PURE__ */ new Set([
725
723
  "FunctionDeclaration",
726
724
  "FunctionExpression",
@@ -896,8 +894,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
896
894
  code.prependLeft(start, `\nimport { runDefers as ${sharedHelper} } from '@swiftuijs/twill-runtime/helpers/v1';\n`);
897
895
  }
898
896
  }
899
- //#endregion
900
- //#region src/branches.ts
897
+ // src/branches.ts
901
898
  function suspension(node) {
902
899
  if (["AwaitExpression", "YieldExpression"].includes(node.type)) return node;
903
900
  if ([
@@ -917,10 +914,53 @@ function suspension(node) {
917
914
  }
918
915
  }
919
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
+ }
920
958
  /** Native switches with per-arm scopes. Direct returns and standalone variable
921
959
  * initializers need no extra function; other expressions keep a lexical IIFE. */
922
- 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()) {
923
961
  const returns = /* @__PURE__ */ new Set();
962
+ const implicit = new Set(implicitReturns);
963
+ const sharedScopes = /* @__PURE__ */ new Set();
924
964
  const initializers = /* @__PURE__ */ new Map();
925
965
  const leadingDoc = (start) => {
926
966
  let low = 0, high = comments.length;
@@ -932,19 +972,6 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
932
972
  const comment = comments[low - 1];
933
973
  return comment && /^\s*$/.test(source.slice(comment.end, start)) ? comment : void 0;
934
974
  };
935
- const directEval = (node) => {
936
- if ([
937
- "FunctionDeclaration",
938
- "FunctionExpression",
939
- "ArrowFunctionExpression"
940
- ].includes(node.type)) return false;
941
- if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
942
- return Object.entries(node).some(([key, value]) => ![
943
- "loc",
944
- "trailing",
945
- "tag"
946
- ].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
947
- };
948
975
  const grouping = (start, end, initializer = false) => {
949
976
  const parentheses = [];
950
977
  const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
@@ -957,6 +984,15 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
957
984
  for (const token of parentheses) code.remove(start + token.start, start + token.end);
958
985
  };
959
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
+ }
960
996
  if (node.type === "VariableDeclaration" && node.declarations.length === 1) {
961
997
  const declaration = node.declarations[0];
962
998
  if (declaration.id.type === "Identifier" && declaration.init?.type === "TwillSwitchExpression" && [
@@ -978,6 +1014,7 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
978
1014
  if (node.type === "ReturnStatement" && node.argument?.type === "TwillSwitchExpression") {
979
1015
  returns.add(node.argument);
980
1016
  code.remove(node.start, node.start + 6);
1017
+ if (source[node.end - 1] === ";") code.remove(node.end - 1, node.end);
981
1018
  for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) grouping(start, end);
982
1019
  }
983
1020
  for (const [key, value] of Object.entries(node)) {
@@ -1000,8 +1037,8 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
1000
1037
  return name;
1001
1038
  };
1002
1039
  for (const node of switches) {
1003
- const direct = returns.has(node);
1004
- const suspended = !direct && suspension(node);
1040
+ const direct = returns.has(node) || implicit.has(node);
1041
+ const suspended = !returns.has(node) && suspension(node);
1005
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.`);
1006
1043
  const initializer = initializers.get(node);
1007
1044
  const result = initializer ? fresh("Result") : void 0;
@@ -1020,7 +1057,10 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
1020
1057
  const valuePrefix = result ? `${result} = (` : "return ";
1021
1058
  const valueSuffix = result ? `); break ${exit};` : ";";
1022
1059
  const kind = node.cases.some((branch) => branch.enumPattern) ? fresh("Kind") : void 0;
1023
- 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
+ }
1024
1064
  code.remove(node.parenStart, node.parenStart + 1);
1025
1065
  if (node.keyword === "match") {
1026
1066
  const tokens = Parser.tokenizer(source.slice(node.discriminant.end, node.parenEnd), { ecmaVersion: "latest" });
@@ -1077,13 +1117,13 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
1077
1117
  }
1078
1118
  }
1079
1119
  const hasDefault = node.cases.some((branch) => !branch.pattern && !branch.enumPattern && branch.test === null);
1080
- const check = !hasDefault && language.startsWith("ts") ? ` ${kind ? `(0 as unknown as typeof ${kind})` : subject} satisfies never;` : "";
1081
- const failure = hasDefault ? "" : `${check} throw new TypeError('Non-exhaustive switch expression');`;
1082
- 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 });
1083
1124
  }
1084
1125
  }
1085
- //#endregion
1086
- //#region src/enums.ts
1126
+ // src/enums.ts
1087
1127
  /** Find free type references, respecting generic function/mapped/infer binders. */
1088
1128
  function references(node, names, found = /* @__PURE__ */ new Set(), bound = /* @__PURE__ */ new Set()) {
1089
1129
  const local = new Set(bound);
@@ -1262,8 +1302,7 @@ function mapEnumCopies(source, generated, map, insertions) {
1262
1302
  }));
1263
1303
  return copies;
1264
1304
  }
1265
- //#endregion
1266
- //#region src/compiler.ts
1305
+ // src/compiler.ts
1267
1306
  function inferLanguage(filename) {
1268
1307
  if (/\.(?:twillx|tsx)$/.test(filename)) return "tsx";
1269
1308
  if (/\.jsx$/.test(filename)) return "jsx";
@@ -1288,6 +1327,25 @@ var TwillSyntaxError = class extends SyntaxError {
1288
1327
  this.frame = `${source.split(/\r\n|[\r\n\u2028\u2029]/)[line - 1] ?? ""}\n${" ".repeat(column)}^`;
1289
1328
  }
1290
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
+ }
1291
1349
  function walk(node, visit) {
1292
1350
  visit(node);
1293
1351
  for (const key of Object.keys(node)) {
@@ -1317,7 +1375,9 @@ function transform(source, options = {}) {
1317
1375
  const code = new MagicString(source);
1318
1376
  const enumInsertions = parsed.enums.length ? lowerEnums(source, parsed.ast, parsed.enums, code) : [];
1319
1377
  const usedNames = /* @__PURE__ */ new Set();
1320
- 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) => {
1321
1381
  if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
1322
1382
  });
1323
1383
  let guardCounter = 0;
@@ -1330,12 +1390,13 @@ function transform(source, options = {}) {
1330
1390
  while (usedNames.has(temporary));
1331
1391
  usedNames.add(temporary);
1332
1392
  const patternEnd = binding.typeAnnotation?.start ?? binding.end;
1333
- code.overwrite(statement.start, binding.start, `{const ${temporary}`);
1393
+ const reuseScope = reusable.has(statement);
1394
+ code.overwrite(statement.start, binding.start, `${reuseScope ? "" : "{"}const ${temporary}`);
1334
1395
  code.move(binding.start, patternEnd, statement.consequent.start + 1);
1335
1396
  code.prependRight(binding.start, "const ");
1336
1397
  code.appendLeft(patternEnd, ` = ${temporary};`);
1337
1398
  code.prependLeft(statement.consequent.start, `; if (${temporary} !== null && ${temporary} !== void 0) `);
1338
- code.appendLeft(statement.end, "}");
1399
+ if (!reuseScope) code.appendLeft(statement.end, "}");
1339
1400
  }
1340
1401
  for (const guard of parsed.guards) {
1341
1402
  if (guard.singleStatement) {
@@ -1344,22 +1405,24 @@ function transform(source, options = {}) {
1344
1405
  }
1345
1406
  if (guard.binding) {
1346
1407
  const binding = guard.binding.declarations[0].id;
1347
- if (binding.type === "Identifier") code.remove(guard.start, guard.start + 5);
1348
- else {
1408
+ let name;
1409
+ if (binding.type === "Identifier") {
1410
+ name = binding.name;
1411
+ code.remove(guard.start, guard.start + 5);
1412
+ } else {
1349
1413
  let temporary;
1350
1414
  do
1351
1415
  temporary = `__twillGuard${guardCounter++}`;
1352
1416
  while (usedNames.has(temporary));
1353
1417
  usedNames.add(temporary);
1418
+ name = temporary;
1354
1419
  const patternEnd = binding.typeAnnotation?.start ?? binding.end;
1355
1420
  code.overwrite(guard.start, binding.start, `const ${temporary}`);
1356
1421
  code.move(binding.start, patternEnd, guard.end);
1357
1422
  code.prependRight(binding.start, "; const ");
1358
1423
  code.appendLeft(patternEnd, ` = ${temporary};`);
1359
- code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${temporary} == null)`);
1360
- continue;
1361
1424
  }
1362
- 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)`);
1363
1426
  } else {
1364
1427
  code.overwrite(guard.start, guard.start + 5, "if (!(");
1365
1428
  code.overwrite(guard.elseStart, guard.elseStart + 4, "))");
@@ -1390,7 +1453,9 @@ function transform(source, options = {}) {
1390
1453
  name = `__twillArg${implicitCounter++}`;
1391
1454
  while (usedNames.has(name));
1392
1455
  usedNames.add(name);
1393
- 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}`);
1394
1459
  for (const offset of metadata.implicitMembers) code.appendLeft(offset, name);
1395
1460
  }
1396
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];
@@ -1418,9 +1483,13 @@ function transform(source, options = {}) {
1418
1483
  return edits.toString();
1419
1484
  };
1420
1485
  let counter = 0;
1486
+ const directReturns = [];
1421
1487
  for (const node of parsed.calls) {
1422
1488
  const { callEnd, hadParens, originalArgs, closures } = node.trailing;
1423
1489
  const component = componentCalls.has(node);
1490
+ const slotObject = vue && (options.vueDefaultSlot !== "function" || closures.length > 1);
1491
+ let nativeChild = false;
1492
+ let bareChild = false;
1424
1493
  if (component) {
1425
1494
  if (originalArgs.length > 1 || originalArgs[0]?.type === "SpreadElement" || node.optional || node.callee.optional) throw new TwillSyntaxError(source, filename, {
1426
1495
  message: "Component syntax accepts one props object and children; optional calls use ordinary callbacks.",
@@ -1445,8 +1514,13 @@ function transform(source, options = {}) {
1445
1514
  labels.add(name);
1446
1515
  }
1447
1516
  }
1448
- const opening = vue ? ">{({default: " : ">{(";
1449
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: " : ">{";
1450
1524
  code.appendLeft(node.callee.start, "(<");
1451
1525
  const props = originalArgs[0];
1452
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)))) {
@@ -1493,7 +1567,7 @@ function transform(source, options = {}) {
1493
1567
  const tokens = Parser.tokenizer(gap, { ecmaVersion: "latest" });
1494
1568
  const first = tokens.getToken();
1495
1569
  const last = tokens.getToken();
1496
- code.overwrite(tagEnd, callEnd, opening + gap.slice(first.end, last.start));
1570
+ code.overwrite(tagEnd, callEnd, opening + (nativeChild ? "" : gap.slice(first.end, last.start)));
1497
1571
  } else code.appendLeft(callEnd, opening);
1498
1572
  } else if (hadParens) {
1499
1573
  const tailStart = originalArgs.at(-1)?.end ?? callEnd - 1;
@@ -1516,24 +1590,40 @@ function transform(source, options = {}) {
1516
1590
  "DeferStatement",
1517
1591
  "EmptyStatement"
1518
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) : "{";
1519
1597
  if (singleContent && body.length === 1 && !vue) {
1520
1598
  directChild = true;
1521
- code.remove(closure.start, closure.start + 1);
1522
- code.remove(closure.end - 1, closure.end);
1523
1599
  const statement = expressions[0];
1524
- 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
+ }
1525
1608
  continue;
1526
1609
  }
1527
1610
  if (metadata.label) {
1528
1611
  const label = source.slice(metadata.label.start, metadata.label.end).replace(/\s*:\s*$/, "");
1529
- 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)}: ` : ", ");
1530
1613
  }
1531
1614
  if (metadata.header) {
1532
1615
  const header = metadata.header;
1533
1616
  code.overwrite(closure.start, closure.start + 1, header.parenthesized || metadata.async ? "" : "(");
1534
1617
  if (metadata.async && !header.parenthesized) code.appendLeft(header.asyncEnd, " (");
1535
- code.overwrite(header.inStart, header.end, `${header.parenthesized ? "" : ") "}=> {`);
1536
- } 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
+ }
1537
1627
  if (content && !singleContent) {
1538
1628
  let collector;
1539
1629
  do
@@ -1586,13 +1676,16 @@ function transform(source, options = {}) {
1586
1676
  body.forEach(collect);
1587
1677
  code.prependLeft(bodyStart, `const ${collector} = [];`);
1588
1678
  code.prependLeft(closure.end - 1, vue ? `;return ${collector};` : `;return ${collector}.length === 1 ? ${collector}[0] : ${collector};`);
1589
- } else if (singleContent || options.implicitReturn !== false && body.length === 1 && body[0].type === "ExpressionStatement") {
1679
+ } else if (returnsValue && !explicitReturn) {
1590
1680
  const statement = singleContent ? expressions[0] : body[0];
1591
- code.prependLeft(statement.start, "return (");
1592
- 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
+ }
1593
1686
  }
1594
1687
  }
1595
- 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)}>)`);
1596
1689
  else code.appendLeft(closures.at(-1).end, ")");
1597
1690
  }
1598
1691
  if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, parsed.comments, (node, message) => {
@@ -1601,7 +1694,7 @@ function transform(source, options = {}) {
1601
1694
  pos: node.start,
1602
1695
  loc: node.loc.start
1603
1696
  });
1604
- });
1697
+ }, directReturns, reusable);
1605
1698
  for (const end of attributeClosings) code.appendLeft(end, "}");
1606
1699
  if (parsed.defers.length) {
1607
1700
  const starts = /* @__PURE__ */ new Map();
@@ -1660,7 +1753,6 @@ function generatedPosition(result, line, column) {
1660
1753
  bias: GREATEST_LOWER_BOUND
1661
1754
  });
1662
1755
  }
1663
- //#endregion
1664
1756
  export { transform as a, originalPosition as i, generatedPosition as n, parse as o, inferLanguage as r, TwillSyntaxError as t };
1665
1757
 
1666
- //# sourceMappingURL=compiler-BdkU4Ruh.js.map
1758
+ //# sourceMappingURL=compiler-DI0H4d1R.js.map