From 77c3310372f33bde430a28291fbe6561f2f3fc1c Mon Sep 17 00:00:00 2001 From: Cold Fry Date: Tue, 7 Apr 2026 10:23:19 +0000 Subject: [PATCH 1/2] centralize tslua options --- cmd/tslua/eval.go | 8 +- cmd/tslua/main.go | 141 ++++++++++++++++++++------------- cmd/tslua/server.go | 15 ++++ cmd/tslua/watch.go | 19 +---- cmd/wasm/main.go | 10 ++- internal/transpiler/classes.go | 1 + 6 files changed, 119 insertions(+), 75 deletions(-) diff --git a/cmd/tslua/eval.go b/cmd/tslua/eval.go index d965f4b..cb7d612 100644 --- a/cmd/tslua/eval.go +++ b/cmd/tslua/eval.go @@ -92,8 +92,12 @@ func runEval(source string) error { return fmt.Errorf("unsupported luaLibImport: %s (supported: require, inline, none)", luaLibImportFlag) } opts := transpiler.TranspileOptions{ - LuaLibImport: luaLibImport, - Trace: traceFlag, + EmitMode: transpiler.EmitMode(emitModeFlag), + NoImplicitSelf: noImplicitSelfFlag, + NoImplicitGlobalVariables: noImplicitGlobalVariablesFlag, + ClassStyle: transpiler.ClassStyle(classStyleFlag), + LuaLibImport: luaLibImport, + Trace: traceFlag, } if luaLibImport == transpiler.LuaLibImportInline { if fd, err := lualib.FeatureDataForTarget(string(luaTarget)); err == nil { diff --git a/cmd/tslua/main.go b/cmd/tslua/main.go index d123409..7bd7da0 100644 --- a/cmd/tslua/main.go +++ b/cmd/tslua/main.go @@ -43,11 +43,15 @@ var ( timingFlag bool watchFlag bool luaLibImportFlag string - socketFlag string - evalSourceFlag string - sourceMapFlag bool - noImplicitSelfFlag bool - traceFlag bool + socketFlag string + evalSourceFlag string + sourceMapFlag bool + sourceMapTracebackFlag bool + inlineSourceMapFlag bool + noImplicitSelfFlag bool + noImplicitGlobalVariablesFlag bool + classStyleFlag string + traceFlag bool ) func main() { @@ -61,22 +65,26 @@ func main() { } rootCmd.SetVersionTemplate("{{.Version}}\n") - // Persistent flags: shared across root and subcommands. + // Persistent flags: shared across root and subcommands (affect transpilation behavior). rootCmd.PersistentFlags().StringVar(&luaTargetFlag, "luaTarget", "JIT", "Lua target version (JIT, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, Luau, universal)") rootCmd.PersistentFlags().StringVar(&diagFormatFlag, "diagnosticFormat", "tstl", "diagnostic code format (tstl, native)") rootCmd.PersistentFlags().StringVar(&cpuprofileFlag, "cpuprofile", "", "write CPU profile to file") rootCmd.PersistentFlags().BoolVar(&timingFlag, "timing", false, "print phase timings to stderr") rootCmd.PersistentFlags().StringVar(&luaLibImportFlag, "luaLibImport", "require", "how lualib features are included (require, inline, none)") + rootCmd.PersistentFlags().StringVar(&emitModeFlag, "emitMode", "tstl", "emit mode: tstl (match TSTL output) or optimized") + rootCmd.PersistentFlags().BoolVar(&noImplicitSelfFlag, "noImplicitSelf", false, "default functions to no-self unless annotated") + rootCmd.PersistentFlags().BoolVar(&noImplicitGlobalVariablesFlag, "noImplicitGlobalVariables", false, "force local declarations in script-mode top-level scope") + rootCmd.PersistentFlags().StringVar(&classStyleFlag, "classStyle", "", "class emit style (tstl, luabind, middleclass, inline)") // Root-only flags: project build mode. rootCmd.Flags().StringVarP(&projectFlag, "project", "p", "", "path to tsconfig.json") rootCmd.Flags().StringVar(&outdirFlag, "outdir", "", "output directory for Lua files (default: stdout)") - rootCmd.Flags().StringVar(&emitModeFlag, "emitMode", "tstl", "emit mode: tstl (match TSTL output) or optimized") rootCmd.Flags().StringVar(&luaBundleFlag, "luaBundle", "", "output all modules as a single bundled Lua file") rootCmd.Flags().StringVar(&luaBundleEntryFlag, "luaBundleEntry", "", "entry point source file for bundle mode") rootCmd.Flags().BoolVar(&exportAsGlobalFlag, "exportAsGlobal", false, "strip module wrapper, emit exports as globals") rootCmd.Flags().BoolVar(&sourceMapFlag, "sourceMap", false, "generate .lua.map source map files") - rootCmd.Flags().BoolVar(&noImplicitSelfFlag, "noImplicitSelf", false, "default functions to no-self unless annotated") + rootCmd.Flags().BoolVar(&sourceMapTracebackFlag, "sourceMapTraceback", false, "register source maps at runtime for debug.traceback rewriting") + rootCmd.Flags().BoolVar(&inlineSourceMapFlag, "inlineSourceMap", false, "embed source map as base64 data URL in Lua output") rootCmd.Flags().BoolVar(&verboseFlag, "verbose", false, "print each output file path") rootCmd.Flags().BoolVarP(&watchFlag, "watch", "w", false, "watch for file changes and rebuild") @@ -165,22 +173,52 @@ func main() { // buildConfig holds resolved settings shared across builds. type buildConfig struct { - cwd string - configDir string - configParseResult *tsoptions.ParsedCommandLine - sourceRoot string - outdir string - diagFormat dw.DiagnosticFormat - luaTarget transpiler.LuaTarget - emitMode transpiler.EmitMode - luaLibImport transpiler.LuaLibImportKind - luaBundle string - luaBundleEntry string - exportAsGlobal bool - exportAsGlobalMatch string - noImplicitSelf bool - sourceMap bool - stderrIsTerminal bool + cwd string + configDir string + configParseResult *tsoptions.ParsedCommandLine + sourceRoot string + outdir string + diagFormat dw.DiagnosticFormat + luaTarget transpiler.LuaTarget + emitMode transpiler.EmitMode + luaLibImport transpiler.LuaLibImportKind + luaBundle string + luaBundleEntry string + exportAsGlobal bool + exportAsGlobalMatch string + noImplicitSelf bool + noImplicitGlobalVariables bool + sourceMap bool + sourceMapTraceback bool + inlineSourceMap bool + classStyle transpiler.ClassStyle + stderrIsTerminal bool +} + +// transpileOpts returns the TranspileOptions derived from this build config. +// This is the single chokepoint for CLI entrypoints — all TranspileOptions +// construction for project builds goes through here. +func (cfg *buildConfig) transpileOpts() transpiler.TranspileOptions { + opts := transpiler.TranspileOptions{ + EmitMode: cfg.emitMode, + ExportAsGlobal: cfg.exportAsGlobal, + ExportAsGlobalMatch: cfg.exportAsGlobalMatch, + NoImplicitSelf: cfg.noImplicitSelf, + NoImplicitGlobalVariables: cfg.noImplicitGlobalVariables, + LuaLibImport: cfg.luaLibImport, + SourceMap: cfg.sourceMap, + SourceMapTraceback: cfg.sourceMapTraceback, + InlineSourceMap: cfg.inlineSourceMap, + ClassStyle: cfg.classStyle, + } + if cfg.luaLibImport == transpiler.LuaLibImportInline { + if fd, err := lualib.FeatureDataForTarget(string(cfg.luaTarget)); err == nil { + opts.LualibFeatureData = fd + } else { + opts.LualibInlineContent = lualibInlineContent(cfg.luaTarget) + } + } + return opts } func run(cmd *cobra.Command, args []string) error { @@ -279,23 +317,29 @@ func run(cmd *cobra.Command, args []string) error { } } + sourceMap := sourceMapFlag || sourceMapTracebackFlag || inlineSourceMapFlag || configParseResult.CompilerOptions().SourceMap.IsTrue() + cfg := &buildConfig{ - cwd: cwd, - configDir: string(configDir), - configParseResult: configParseResult, - sourceRoot: sourceRoot, - outdir: outdir, - diagFormat: diagFormat, - luaTarget: luaTarget, - emitMode: emitMode, - luaLibImport: luaLibImport, - luaBundle: luaBundleFlag, - luaBundleEntry: luaBundleEntryFlag, - exportAsGlobal: exportAsGlobal, - exportAsGlobalMatch: exportAsGlobalMatch, - noImplicitSelf: noImplicitSelfFlag, - sourceMap: sourceMapFlag || configParseResult.CompilerOptions().SourceMap.IsTrue(), - stderrIsTerminal: stderrIsTerminal, + cwd: cwd, + configDir: string(configDir), + configParseResult: configParseResult, + sourceRoot: sourceRoot, + outdir: outdir, + diagFormat: diagFormat, + luaTarget: luaTarget, + emitMode: emitMode, + luaLibImport: luaLibImport, + luaBundle: luaBundleFlag, + luaBundleEntry: luaBundleEntryFlag, + exportAsGlobal: exportAsGlobal, + exportAsGlobalMatch: exportAsGlobalMatch, + noImplicitSelf: noImplicitSelfFlag, + noImplicitGlobalVariables: noImplicitGlobalVariablesFlag, + sourceMap: sourceMap, + sourceMapTraceback: sourceMapTracebackFlag, + inlineSourceMap: inlineSourceMapFlag, + classStyle: transpiler.ClassStyle(classStyleFlag), + stderrIsTerminal: stderrIsTerminal, } if watchFlag { @@ -326,22 +370,7 @@ func runOnce(cfg *buildConfig, host compiler.CompilerHost) error { tCheck := time.Now() - opts := transpiler.TranspileOptions{ - EmitMode: cfg.emitMode, - ExportAsGlobal: cfg.exportAsGlobal, - ExportAsGlobalMatch: cfg.exportAsGlobalMatch, - NoImplicitSelf: cfg.noImplicitSelf, - LuaLibImport: cfg.luaLibImport, - SourceMap: cfg.sourceMap, - } - if cfg.luaLibImport == transpiler.LuaLibImportInline { - if fd, err := lualib.FeatureDataForTarget(string(cfg.luaTarget)); err == nil { - opts.LualibFeatureData = fd - } else { - opts.LualibInlineContent = lualibInlineContent(cfg.luaTarget) - } - } - results, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, nil, opts) + results, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, nil, cfg.transpileOpts()) hasErrors := reportDiagnostics(cfg, semanticDiags, transpileDiags) diff --git a/cmd/tslua/server.go b/cmd/tslua/server.go index d2cf18b..e855742 100644 --- a/cmd/tslua/server.go +++ b/cmd/tslua/server.go @@ -530,6 +530,9 @@ func transpileOptsFromRequest(req serverRequest) transpiler.TranspileOptions { if v, ok := req.CompilerOptions["noImplicitSelf"].(bool); ok && v { opts.NoImplicitSelf = true } + if v, ok := req.CompilerOptions["noImplicitGlobalVariables"].(bool); ok && v { + opts.NoImplicitGlobalVariables = true + } if v, ok := req.CompilerOptions["sourceMap"].(bool); ok && v { opts.SourceMap = true } @@ -541,6 +544,18 @@ func transpileOptsFromRequest(req serverRequest) transpiler.TranspileOptions { opts.InlineSourceMap = true opts.SourceMap = true // inline requires source map generation } + if v, ok := req.CompilerOptions["emitMode"].(string); ok && v != "" { + opts.EmitMode = transpiler.EmitMode(v) + } + if v, ok := req.CompilerOptions["classStyle"].(string); ok && v != "" { + opts.ClassStyle = transpiler.ClassStyle(v) + } + if v, ok := req.CompilerOptions["luaLibImport"].(string); ok && v != "" { + opts.LuaLibImport = transpiler.LuaLibImportKind(v) + } + if v, ok := req.CompilerOptions["exportAsGlobal"].(bool); ok && v { + opts.ExportAsGlobal = true + } return opts } diff --git a/cmd/tslua/watch.go b/cmd/tslua/watch.go index 30a4b52..d7b166a 100644 --- a/cmd/tslua/watch.go +++ b/cmd/tslua/watch.go @@ -44,12 +44,7 @@ func runWatch(cfg *buildConfig, host compiler.CompilerHost) error { semanticDiags := compiler.SortAndDeduplicateDiagnostics( incremental.Program_GetSemanticDiagnostics(incrProg, context.Background(), nil), ) - results, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, nil, transpiler.TranspileOptions{ - EmitMode: cfg.emitMode, - ExportAsGlobal: cfg.exportAsGlobal, - ExportAsGlobalMatch: cfg.exportAsGlobalMatch, - NoImplicitSelf: cfg.noImplicitSelf, - }) + results, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, nil, cfg.transpileOpts()) for _, r := range results { cachedResults[r.FileName] = r } @@ -189,11 +184,7 @@ func runWatch(cfg *buildConfig, host compiler.CompilerHost) error { tCheck := time.Now() - freshResults, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, changedFiles, transpiler.TranspileOptions{ - EmitMode: cfg.emitMode, - ExportAsGlobal: cfg.exportAsGlobal, - ExportAsGlobalMatch: cfg.exportAsGlobalMatch, - }) + freshResults, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, changedFiles, cfg.transpileOpts()) for _, r := range freshResults { cachedResults[r.FileName] = r } @@ -237,11 +228,7 @@ func runWatch(cfg *buildConfig, host compiler.CompilerHost) error { fmt.Fprintf(os.Stderr, "build finished in %.2fms\n", msf(time.Since(t0))) } else { // Async path: transpile+write immediately, incr+check in background. - freshResults, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, changedFiles, transpiler.TranspileOptions{ - EmitMode: cfg.emitMode, - ExportAsGlobal: cfg.exportAsGlobal, - ExportAsGlobalMatch: cfg.exportAsGlobalMatch, - }) + freshResults, transpileDiags := transpiler.TranspileProgramWithOptions(program, cfg.sourceRoot, cfg.luaTarget, changedFiles, cfg.transpileOpts()) for _, r := range freshResults { cachedResults[r.FileName] = r } diff --git a/cmd/wasm/main.go b/cmd/wasm/main.go index 892d2a7..9ca5ddb 100644 --- a/cmd/wasm/main.go +++ b/cmd/wasm/main.go @@ -128,12 +128,16 @@ type transpileResult struct { // wasmOptions holds the JSON structure passed from the JS side. type wasmOptions struct { CompilerOptions map[string]any `json:"compilerOptions,omitempty"` - TSTL struct { + TSTL struct { LuaTarget string `json:"luaTarget,omitempty"` EmitMode string `json:"emitMode,omitempty"` ClassStyle string `json:"classStyle,omitempty"` NoImplicitSelf bool `json:"noImplicitSelf,omitempty"` NoImplicitGlobalVariables bool `json:"noImplicitGlobalVariables,omitempty"` + ExportAsGlobal bool `json:"exportAsGlobal,omitempty"` + SourceMap bool `json:"sourceMap,omitempty"` + SourceMapTraceback bool `json:"sourceMapTraceback,omitempty"` + InlineSourceMap bool `json:"inlineSourceMap,omitempty"` Trace bool `json:"trace,omitempty"` } `json:"tstl,omitempty"` } @@ -197,6 +201,10 @@ func transpile(tsCode string, wopts wasmOptions) transpileResult { ClassStyle: transpiler.ClassStyle(wopts.TSTL.ClassStyle), NoImplicitSelf: wopts.TSTL.NoImplicitSelf, NoImplicitGlobalVariables: wopts.TSTL.NoImplicitGlobalVariables, + ExportAsGlobal: wopts.TSTL.ExportAsGlobal, + SourceMap: wopts.TSTL.SourceMap || wopts.TSTL.SourceMapTraceback || wopts.TSTL.InlineSourceMap, + SourceMapTraceback: wopts.TSTL.SourceMapTraceback, + InlineSourceMap: wopts.TSTL.InlineSourceMap, Trace: wopts.TSTL.Trace, } if fd, err := lualib.FeatureDataForTarget(string(lt)); err == nil { diff --git a/internal/transpiler/classes.go b/internal/transpiler/classes.go index fc9c455..f60b422 100644 --- a/internal/transpiler/classes.go +++ b/internal/transpiler/classes.go @@ -705,6 +705,7 @@ func (t *Transpiler) transformClassConstructor(classRef lua.Expression, construc // Constructor: param defaults, super call, param properties, field inits, body if constructor != nil { ctor := constructor.AsConstructorDeclaration() + t.computeOptimizedVarArgs(ctor.Parameters, ctor.Body, false) bodyStmts = append(bodyStmts, t.transformParamPreamble(ctor.Parameters)...) // Transform the constructor body and split out the super call From d4d4f6bb791041e2c2b87ceac8fcd118b3447c13 Mon Sep 17 00:00:00 2001 From: Cold Fry Date: Tue, 7 Apr 2026 10:26:45 +0000 Subject: [PATCH 2/2] more fixes --- cmd/tslua/main.go | 26 +++++++++++++------------- cmd/wasm/main.go | 2 +- extern/tstl-test-util.patch | 3 ++- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/cmd/tslua/main.go b/cmd/tslua/main.go index 7bd7da0..556cfec 100644 --- a/cmd/tslua/main.go +++ b/cmd/tslua/main.go @@ -30,19 +30,19 @@ var ( ) var ( - projectFlag string - outdirFlag string - luaTargetFlag string - emitModeFlag string - diagFormatFlag string - cpuprofileFlag string - luaBundleFlag string - luaBundleEntryFlag string - exportAsGlobalFlag bool - verboseFlag bool - timingFlag bool - watchFlag bool - luaLibImportFlag string + projectFlag string + outdirFlag string + luaTargetFlag string + emitModeFlag string + diagFormatFlag string + cpuprofileFlag string + luaBundleFlag string + luaBundleEntryFlag string + exportAsGlobalFlag bool + verboseFlag bool + timingFlag bool + watchFlag bool + luaLibImportFlag string socketFlag string evalSourceFlag string sourceMapFlag bool diff --git a/cmd/wasm/main.go b/cmd/wasm/main.go index 9ca5ddb..7a9a2bb 100644 --- a/cmd/wasm/main.go +++ b/cmd/wasm/main.go @@ -128,7 +128,7 @@ type transpileResult struct { // wasmOptions holds the JSON structure passed from the JS side. type wasmOptions struct { CompilerOptions map[string]any `json:"compilerOptions,omitempty"` - TSTL struct { + TSTL struct { LuaTarget string `json:"luaTarget,omitempty"` EmitMode string `json:"emitMode,omitempty"` ClassStyle string `json:"classStyle,omitempty"` diff --git a/extern/tstl-test-util.patch b/extern/tstl-test-util.patch index 500540a..d447ccc 100644 --- a/extern/tstl-test-util.patch +++ b/extern/tstl-test-util.patch @@ -27,7 +27,7 @@ index 2871f6e..219208c 100644 const program = this.getProgram(); const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); const collector = createEmitOutputCollector(this.options.extension); -@@ -256,6 +267,228 @@ export abstract class TestBuilder { +@@ -256,6 +267,229 @@ export abstract class TestBuilder { return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; } @@ -116,6 +116,7 @@ index 2871f6e..219208c 100644 + jsxFactory: this.options.jsxFactory, + jsxFragmentFactory: this.options.jsxFragmentFactory, + noImplicitSelf: this.options.noImplicitSelf, ++ noImplicitGlobalVariables: this.options.noImplicitGlobalVariables, + }, + }); +