11import * as path from "path" ;
2- import { EmitHost } from "./transpilation" ;
2+ import { EmitHost , transpileProject } from "./transpilation" ;
33import * as lua from "./LuaAST" ;
4- import { LuaTarget } from "./CompilerOptions" ;
4+ import { LuaTarget , type CompilerOptions } from "./CompilerOptions" ;
55import { getOrUpdate } from "./utils" ;
6+ import { createEmitOutputCollector , type TranspiledFile } from "./transpilation/output-collector" ;
7+ import { parseConfigFileWithSystem } from "./cli/tsconfig" ;
8+ import { createDiagnosticReporter } from "./cli/report" ;
69
710export enum LuaLibFeature {
811ArrayAt = "ArrayAt" ,
@@ -139,12 +142,16 @@ export function resolveLuaLibDir(luaTarget: LuaTarget) {
139142export const luaLibModulesInfoFileName = "lualib_module_info.json" ;
140143const luaLibModulesInfo = new Map < LuaTarget , LuaLibModulesInfo > ( ) ;
141144
142- export function getLuaLibModulesInfo ( luaTarget :LuaTarget , emitHost :EmitHost ) :LuaLibModulesInfo {
143- if ( ! luaLibModulesInfo . has ( luaTarget ) ) {
145+ export function getLuaLibModulesInfo ( luaTarget :LuaTarget , emitHost :EmitHost , useCache = true ) :LuaLibModulesInfo {
146+ if ( ! useCache || ! luaLibModulesInfo . has ( luaTarget ) ) {
144147const lualibPath = path . join ( resolveLuaLibDir ( luaTarget ) , luaLibModulesInfoFileName ) ;
145148const result = emitHost . readFile ( lualibPath ) ;
146149if ( result !== undefined ) {
147- luaLibModulesInfo . set ( luaTarget , JSON . parse ( result ) as LuaLibModulesInfo ) ;
150+ const info = JSON . parse ( result ) as LuaLibModulesInfo ;
151+ if ( ! useCache ) {
152+ return info ;
153+ }
154+ luaLibModulesInfo . set ( luaTarget , info ) ;
148155} else {
149156throw new Error ( `Could not load lualib dependencies from '${ lualibPath } '` ) ;
150157}
@@ -175,7 +182,21 @@ export function getLuaLibExportToFeatureMap(
175182
176183const lualibFeatureCache = new Map < LuaTarget , Map < LuaLibFeature , string > > ( ) ;
177184
178- export function readLuaLibFeature ( feature :LuaLibFeature , luaTarget :LuaTarget , emitHost :EmitHost ) :string {
185+ export function readLuaLibFeature (
186+ feature :LuaLibFeature ,
187+ luaTarget :LuaTarget ,
188+ emitHost :EmitHost ,
189+ useCache = true
190+ ) :string {
191+ if ( ! useCache ) {
192+ const featurePath = path . join ( resolveLuaLibDir ( luaTarget ) , `${ feature } .lua` ) ;
193+ const luaLibFeature = emitHost . readFile ( featurePath ) ;
194+ if ( luaLibFeature === undefined ) {
195+ throw new Error ( `Could not load lualib feature from '${ featurePath } '` ) ;
196+ }
197+ return luaLibFeature ;
198+ }
199+
179200const featureMap = getOrUpdate ( lualibFeatureCache , luaTarget , ( ) => new Map ( ) ) ;
180201if ( ! featureMap . has ( feature ) ) {
181202const featurePath = path . join ( resolveLuaLibDir ( luaTarget ) , `${ feature } .lua` ) ;
@@ -257,9 +278,68 @@ export function loadImportedLualibFeatures(
257278return statements ;
258279}
259280
281+ const recompileLualibCache = new WeakMap < EmitHost , TranspiledFile [ ] > ( ) ;
282+
283+ function recompileLuaLibFiles ( sourceOptions :CompilerOptions , emitHost :EmitHost ) :TranspiledFile [ ] {
284+ let transpiledFiles = recompileLualibCache . get ( emitHost ) ;
285+ if ( ! transpiledFiles ) {
286+ const tsconfigPath =
287+ sourceOptions . luaTarget === LuaTarget . Lua50
288+ ?path . join ( __dirname , "./lualib/tsconfig.lua50.json" )
289+ :path . join ( __dirname , "./lualib/tsconfig.json" ) ;
290+ const config = parseConfigFileWithSystem ( tsconfigPath ) ;
291+ const options = config . options ;
292+ options . luaPlugins = [ ...( options . luaPlugins ?? [ ] ) , ...( sourceOptions . luaPlugins ?? [ ] ) ] ;
293+
294+ const collector = createEmitOutputCollector ( options . extension ) ;
295+ const reportDiagnostic = createDiagnosticReporter ( false ) ;
296+
297+ const { diagnostics} = transpileProject ( tsconfigPath , options , collector . writeFile ) ;
298+ diagnostics . forEach ( reportDiagnostic ) ;
299+
300+ transpiledFiles = collector . files ;
301+ recompileLualibCache . set ( emitHost , transpiledFiles ) ;
302+ }
303+
304+ return transpiledFiles ;
305+ }
306+
307+ function recompileLuaLibBundle ( sourceOptions :CompilerOptions , emitHost :EmitHost ) :string | undefined {
308+ const transpiledFiles = recompileLuaLibFiles ( sourceOptions , emitHost ) ;
309+ const lualibBundle = transpiledFiles . find ( f => f . outPath . endsWith ( "lualib_bundle.lua" ) ) ;
310+ return lualibBundle ?. lua ;
311+ }
312+
313+ export function recompileInlineLualibFeatures (
314+ features :Iterable < LuaLibFeature > ,
315+ options :CompilerOptions ,
316+ emitHost :EmitHost
317+ ) :string {
318+ const luaTarget = options . luaTarget ?? LuaTarget . Universal ;
319+ const transpiledFiles = recompileLuaLibFiles ( options , emitHost ) ;
320+ emitHost = {
321+ readFile ( filePath :string ) {
322+ const file = transpiledFiles . find ( f => f . outPath === filePath ) ;
323+ return file ?file . text :undefined ;
324+ } ,
325+ } as any as EmitHost ;
326+ const moduleInfo = getLuaLibModulesInfo ( luaTarget , emitHost , false ) ;
327+ return resolveRecursiveLualibFeatures ( features , luaTarget , emitHost , moduleInfo )
328+ . map ( feature => readLuaLibFeature ( feature , luaTarget , emitHost , false ) )
329+ . join ( "\n" ) ;
330+ }
331+
260332const luaLibBundleContent = new Map < string , string > ( ) ;
261333
262- export function getLuaLibBundle ( luaTarget :LuaTarget , emitHost :EmitHost ) :string {
334+ export function getLuaLibBundle ( luaTarget :LuaTarget , emitHost :EmitHost , options :CompilerOptions ) :string {
335+ if ( options . recompileLuaLib ) {
336+ const result = recompileLuaLibBundle ( options , emitHost ) ;
337+ if ( ! result ) {
338+ throw new Error ( `Failed to recompile lualib bundle` ) ;
339+ }
340+ return result ;
341+ }
342+
263343const lualibPath = path . join ( resolveLuaLibDir ( luaTarget ) , "lualib_bundle.lua" ) ;
264344if ( ! luaLibBundleContent . has ( lualibPath ) ) {
265345const result = emitHost . readFile ( lualibPath ) ;
@@ -279,13 +359,26 @@ export function getLualibBundleReturn(exportedValues: string[]): string {
279359
280360export function buildMinimalLualibBundle (
281361features :Iterable < LuaLibFeature > ,
282- luaTarget : LuaTarget ,
362+ options : CompilerOptions ,
283363emitHost :EmitHost
284364) :string {
285- const code = loadInlineLualibFeatures ( features , luaTarget , emitHost ) ;
286- const moduleInfo = getLuaLibModulesInfo ( luaTarget , emitHost ) ;
287- const exports = Array . from ( features ) . flatMap ( feature => moduleInfo [ feature ] . exports ) ;
365+ const luaTarget = options . luaTarget ?? LuaTarget . Universal ;
366+ let code ;
367+ if ( options . recompileLuaLib ) {
368+ code = recompileInlineLualibFeatures ( features , options , emitHost ) ;
369+ const transpiledFiles = recompileLuaLibFiles ( options , emitHost ) ;
370+ emitHost = {
371+ readFile ( filePath :string ) {
372+ const file = transpiledFiles . find ( f => f . outPath === filePath ) ;
373+ return file ?file . text :undefined ;
374+ } ,
375+ } as any as EmitHost ;
376+ } else {
377+ code = loadInlineLualibFeatures ( features , luaTarget , emitHost ) ;
378+ }
288379
380+ const moduleInfo = getLuaLibModulesInfo ( luaTarget , emitHost , ! options . recompileLuaLib ) ;
381+ const exports = Array . from ( features ) . flatMap ( feature => moduleInfo [ feature ] . exports ) ;
289382return code + getLualibBundleReturn ( exports ) ;
290383}
291384