From 558b7b404fd0a0bf5e3f985a11e7e49241d20d73 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 14:01:31 -0800 Subject: [PATCH 01/12] Basic lualib as modules --- build-lualib.js | 9 +- src/CompilerOptions.ts | 1 + src/LuaAST.ts | 8 +- src/LuaLib.ts | 133 +++++++++--------- src/LuaPrinter.ts | 16 ++- src/cli/parse.ts | 5 + src/lualib/ArrayConcat.ts | 2 +- src/lualib/ArrayEntries.ts | 2 +- src/lualib/ArrayEvery.ts | 2 +- src/lualib/ArrayFilter.ts | 2 +- src/lualib/ArrayFind.ts | 2 +- src/lualib/ArrayFindIndex.ts | 2 +- src/lualib/ArrayFlat.ts | 2 +- src/lualib/ArrayFlatMap.ts | 2 +- src/lualib/ArrayForEach.ts | 2 +- src/lualib/ArrayIncludes.ts | 2 +- src/lualib/ArrayIndexOf.ts | 2 +- src/lualib/ArrayIsArray.ts | 2 +- src/lualib/ArrayJoin.ts | 2 +- src/lualib/ArrayMap.ts | 6 +- src/lualib/ArrayPush.ts | 2 +- src/lualib/ArrayReduce.ts | 2 +- src/lualib/ArrayReduceRight.ts | 2 +- src/lualib/ArrayReverse.ts | 2 +- src/lualib/ArraySetLength.ts | 2 +- src/lualib/ArrayShift.ts | 2 +- src/lualib/ArraySlice.ts | 2 +- src/lualib/ArraySome.ts | 2 +- src/lualib/ArraySort.ts | 2 +- src/lualib/ArraySplice.ts | 2 +- src/lualib/ArrayToObject.ts | 2 +- src/lualib/ArrayUnshift.ts | 2 +- src/lualib/Await.ts | 6 +- src/lualib/Class.ts | 2 +- src/lualib/ClassExtends.ts | 2 +- src/lualib/CloneDescriptor.ts | 2 +- src/lualib/Decorate.ts | 11 +- src/lualib/DecorateParam.ts | 4 +- src/lualib/Decorator.d.ts | 5 + src/lualib/DelegatedYield.ts | 4 +- src/lualib/Delete.ts | 4 +- src/lualib/Error.ts | 26 ++-- src/lualib/FunctionBind.ts | 2 +- src/lualib/Generator.ts | 16 +-- src/lualib/GeneratorIterator.d.ts | 5 + src/lualib/InstanceOf.ts | 2 +- src/lualib/InstanceOfObject.ts | 2 +- src/lualib/Iterator.ts | 16 ++- src/lualib/Map.ts | 4 +- src/lualib/MathAtan2.ts | 2 +- src/lualib/MathSign.ts | 2 +- src/lualib/New.ts | 2 +- src/lualib/Number.ts | 2 +- src/lualib/NumberIsFinite.ts | 2 +- src/lualib/NumberIsNaN.ts | 2 +- src/lualib/NumberToString.ts | 8 +- src/lualib/ObjectAssign.ts | 2 +- src/lualib/ObjectDefineProperty.ts | 9 +- src/lualib/ObjectEntries.ts | 2 +- src/lualib/ObjectFromEntries.ts | 2 +- src/lualib/ObjectGetOwnPropertyDescriptor.ts | 6 +- src/lualib/ObjectGetOwnPropertyDescriptors.ts | 5 +- src/lualib/ObjectKeys.ts | 2 +- src/lualib/ObjectRest.ts | 2 +- src/lualib/ObjectValues.ts | 2 +- src/lualib/ParseFloat.ts | 2 +- src/lualib/ParseInt.ts | 8 +- src/lualib/Promise.ts | 15 +- src/lualib/PromiseAll.ts | 4 +- src/lualib/PromiseAllSettled.ts | 4 +- src/lualib/PromiseAny.ts | 4 +- src/lualib/PromiseRace.ts | 4 +- src/lualib/Set.ts | 4 +- src/lualib/SetDescriptor.ts | 18 ++- src/lualib/SourceMapTraceBack.ts | 9 +- src/lualib/SparseArray.d.ts | 1 + src/lualib/SparseArrayNew.ts | 4 +- src/lualib/SparseArrayPush.ts | 4 +- src/lualib/SparseArraySpread.ts | 4 +- src/lualib/Spread.ts | 2 +- src/lualib/StringAccess.ts | 2 +- src/lualib/StringCharAt.ts | 2 +- src/lualib/StringCharCodeAt.ts | 2 +- src/lualib/StringConcat.ts | 2 +- src/lualib/StringEndsWith.ts | 2 +- src/lualib/StringIncludes.ts | 2 +- src/lualib/StringPadEnd.ts | 2 +- src/lualib/StringPadStart.ts | 2 +- src/lualib/StringReplace.ts | 2 +- src/lualib/StringReplaceAll.ts | 2 +- src/lualib/StringSlice.ts | 2 +- src/lualib/StringSplit.ts | 2 +- src/lualib/StringStartsWith.ts | 2 +- src/lualib/StringSubstr.ts | 2 +- src/lualib/StringSubstring.ts | 2 +- src/lualib/StringTrim.ts | 2 +- src/lualib/StringTrimEnd.ts | 2 +- src/lualib/StringTrimStart.ts | 2 +- src/lualib/Symbol.ts | 8 +- src/lualib/SymbolRegistry.ts | 17 +-- src/lualib/TypeOf.ts | 2 +- src/lualib/Unpack.ts | 2 +- src/lualib/WeakMap.ts | 4 +- src/lualib/WeakSet.ts | 4 +- src/lualib/tsconfig.json | 3 +- src/transformation/builtins/index.ts | 18 +-- src/transformation/builtins/promise.ts | 4 - src/transformation/utils/lua-ast.ts | 16 ++- src/transformation/visitors/class/setup.ts | 2 +- src/transformation/visitors/identifier.ts | 10 +- src/transformation/visitors/modules/import.ts | 32 +++-- src/transformation/visitors/sourceFile.ts | 38 ++++- src/transpilation/bundle.ts | 1 + src/transpilation/lualib.ts | 67 +++++++++ src/transpilation/transpile.ts | 19 +++ src/transpilation/transpiler.ts | 11 +- src/transpilation/utils.ts | 1 + test/transpile/lualib.spec.ts | 26 +--- test/transpile/module-resolution.spec.ts | 2 +- 119 files changed, 501 insertions(+), 302 deletions(-) create mode 100644 src/lualib/Decorator.d.ts create mode 100644 src/lualib/GeneratorIterator.d.ts create mode 100644 src/lualib/SparseArray.d.ts create mode 100644 src/transpilation/lualib.ts diff --git a/build-lualib.js b/build-lualib.js index 41ece7e32..8a35f147b 100644 --- a/build-lualib.js +++ b/build-lualib.js @@ -3,15 +3,8 @@ const fs = require("fs"); const path = require("path"); const ts = require("typescript"); const tstl = require("./src"); -const { loadLuaLibFeatures } = require("./src/LuaLib"); +const { loadInlineLualibFeatures } = require("./src/LuaLib"); const configFileName = path.resolve(__dirname, "src/lualib/tsconfig.json"); const { diagnostics } = tstl.transpileProject(configFileName); diagnostics.forEach(tstl.createDiagnosticReporter(true)); - -const bundlePath = path.join(__dirname, "dist/lualib/lualib_bundle.lua"); -if (fs.existsSync(bundlePath)) { - fs.unlinkSync(bundlePath); -} - -fs.writeFileSync(bundlePath, loadLuaLibFeatures(Object.values(tstl.LuaLibFeature), ts.sys)); diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 952de19ef..fde2cb593 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -27,6 +27,7 @@ export type CompilerOptions = OmitIndexSignature & { luaBundleEntry?: string; luaTarget?: LuaTarget; luaLibImport?: LuaLibImportKind; + luaLibProject?: boolean; luaPlugins?: LuaPluginImport[]; noImplicitSelf?: boolean; noHeader?: boolean; diff --git a/src/LuaAST.ts b/src/LuaAST.ts index ee5c9d329..bc8137d67 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -5,7 +5,7 @@ // because we don't create the AST from text import * as ts from "typescript"; -import { LuaLibFeature } from "./transformation/utils/lualib"; +import { LuaLibFeature } from "./LuaLib"; import { castArray } from "./utils"; export enum SyntaxKind { @@ -195,6 +195,8 @@ export interface File extends Node { statements: Statement[]; luaLibFeatures: Set; trivia: string; + // Only used in lualibCompile, this is the values the lua lib feature exports/loaded when imported + exports?: string[]; } export function isFile(node: Node): node is File { @@ -205,12 +207,14 @@ export function createFile( statements: Statement[], luaLibFeatures: Set, trivia: string, - tsOriginal?: ts.Node + tsOriginal?: ts.Node, + exports?: string[] ): File { const file = createNode(SyntaxKind.File, tsOriginal) as File; file.statements = statements; file.luaLibFeatures = luaLibFeatures; file.trivia = trivia; + file.exports = exports; return file; } diff --git a/src/LuaLib.ts b/src/LuaLib.ts index e4321d181..e8fd80226 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -1,5 +1,6 @@ import * as path from "path"; import { EmitHost } from "./transpilation"; +import * as lua from "./LuaAST"; export enum LuaLibFeature { ArrayConcat = "ArrayConcat", @@ -99,81 +100,51 @@ export enum LuaLibFeature { Unpack = "Unpack", } -/* eslint-disable @typescript-eslint/naming-convention */ -const luaLibDependencies: Partial> = { - ArrayConcat: [LuaLibFeature.ArrayIsArray], - ArrayFlat: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray], - ArrayFlatMap: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray], - Await: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise], - Decorate: [LuaLibFeature.ObjectGetOwnPropertyDescriptor, LuaLibFeature.SetDescriptor, LuaLibFeature.ObjectAssign], - DelegatedYield: [LuaLibFeature.StringAccess], - Delete: [LuaLibFeature.ObjectGetOwnPropertyDescriptors, LuaLibFeature.Error, LuaLibFeature.New], - Error: [LuaLibFeature.Class, LuaLibFeature.ClassExtends, LuaLibFeature.New], - FunctionBind: [LuaLibFeature.Unpack], - Generator: [LuaLibFeature.Symbol], - InstanceOf: [LuaLibFeature.Symbol], - Iterator: [LuaLibFeature.Symbol], - NumberToString: [LuaLibFeature.StringAccess], - ObjectDefineProperty: [LuaLibFeature.CloneDescriptor, LuaLibFeature.SetDescriptor], - ObjectFromEntries: [LuaLibFeature.Iterator, LuaLibFeature.Symbol], - Promise: [ - LuaLibFeature.ArrayPush, - LuaLibFeature.Class, - LuaLibFeature.FunctionBind, - LuaLibFeature.InstanceOf, - LuaLibFeature.New, - ], - PromiseAll: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise, LuaLibFeature.Iterator], - PromiseAllSettled: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise, LuaLibFeature.Iterator], - PromiseAny: [ - LuaLibFeature.ArrayPush, - LuaLibFeature.InstanceOf, - LuaLibFeature.New, - LuaLibFeature.Promise, - LuaLibFeature.Iterator, - ], - PromiseRace: [ - LuaLibFeature.ArrayPush, - LuaLibFeature.InstanceOf, - LuaLibFeature.New, - LuaLibFeature.Promise, - LuaLibFeature.Iterator, - ], - ParseFloat: [LuaLibFeature.StringAccess], - ParseInt: [LuaLibFeature.StringSubstr, LuaLibFeature.StringSubstring], - SetDescriptor: [LuaLibFeature.CloneDescriptor], - Spread: [LuaLibFeature.Iterator, LuaLibFeature.StringAccess, LuaLibFeature.Unpack], - StringSplit: [LuaLibFeature.StringSubstring, LuaLibFeature.StringAccess], - SymbolRegistry: [LuaLibFeature.Symbol], - - Map: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - Set: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - WeakMap: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - WeakSet: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], -}; -/* eslint-enable @typescript-eslint/naming-convention */ - -export function loadLuaLibFeatures(features: Iterable, emitHost: EmitHost): string { +export interface LuaLibFeatureInfo { + dependencies?: LuaLibFeature[]; + exports: string[]; +} +export type LuaLibModulesInfo = Record; + +let luaLibModuleInfo: LuaLibModulesInfo | undefined; +export function getLuaLibModuleInfo(emitHost: EmitHost): LuaLibModulesInfo { + if (luaLibModuleInfo === undefined) { + const lualibPath = path.resolve(__dirname, "../dist/lualib/lualib_dependencies.json"); + const result = emitHost.readFile(lualibPath); + if (result !== undefined) { + luaLibModuleInfo = JSON.parse(result) as LuaLibModulesInfo; + } else { + throw new Error(`Could not load lualib dependencies from '${lualibPath}'`); + } + } + return luaLibModuleInfo; +} + +export function readLuaLibFeature(feature: LuaLibFeature, emitHost: EmitHost): string { + const featurePath = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); + const luaLibFeature = emitHost.readFile(featurePath); + if (luaLibFeature === undefined) { + throw new Error(`Could not load lualib feature from '${featurePath}'`); + } + return luaLibFeature; +} + +export function loadInlineLualibFeatures(features: Iterable, emitHost: EmitHost): string { let result = ""; const loadedFeatures = new Set(); + const luaLibDependencyMap = getLuaLibModuleInfo(emitHost); function load(feature: LuaLibFeature): void { if (loadedFeatures.has(feature)) return; loadedFeatures.add(feature); - const dependencies = luaLibDependencies[feature]; + const dependencies = luaLibDependencyMap[feature]?.dependencies; if (dependencies) { dependencies.forEach(load); } - - const featurePath = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); - const luaLibFeature = emitHost.readFile(featurePath); - if (luaLibFeature !== undefined) { - result += luaLibFeature + "\n"; - } else { - throw new Error(`Could not load lualib feature from '${featurePath}'`); - } + const luaLibFeature = readLuaLibFeature(feature, emitHost); + result += luaLibFeature + "\n"; } for (const feature of features) { @@ -183,6 +154,40 @@ export function loadLuaLibFeatures(features: Iterable, emitHost: return result; } +export function loadImportedLualibFeatures( + features: Iterable, + emitHost: EmitHost, + alwaysRequire = false +): lua.Statement[] { + const luaLibModuleInfo = getLuaLibModuleInfo(emitHost); + + const imports = Array.from(features).flatMap(feature => luaLibModuleInfo[feature].exports); + + const requireCall = lua.createCallExpression(lua.createIdentifier("require"), [ + lua.createStringLiteral("lualib_bundle"), + ]); + if (imports.length === 0) { + if (alwaysRequire) { + return [lua.createExpressionStatement(requireCall)]; + } + return []; + } + + const luaLibId = lua.createIdentifier("____lualib"); + const importStatement = lua.createVariableDeclarationStatement(luaLibId, requireCall); + const statements: lua.Statement[] = [importStatement]; + // local = ____luaLib. + for (const item of imports) { + statements.push( + lua.createVariableDeclarationStatement( + lua.createIdentifier(item), + lua.createTableIndexExpression(luaLibId, lua.createStringLiteral(item)) + ) + ); + } + return statements; +} + let luaLibBundleContent: string; export function getLuaLibBundle(emitHost: EmitHost): string { if (luaLibBundleContent === undefined) { diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index dc75b4a9b..0d4863ac3 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -1,12 +1,11 @@ import * as path from "path"; import { Mapping, SourceMapGenerator, SourceNode } from "source-map"; -import { getEmitPath } from "."; import * as ts from "typescript"; import { CompilerOptions, isBundleEnabled, LuaLibImportKind } from "./CompilerOptions"; import * as lua from "./LuaAST"; -import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib"; +import { loadInlineLualibFeatures, LuaLibFeature, loadImportedLualibFeatures } from "./LuaLib"; import { isValidLuaIdentifier, shouldAllowUnicode } from "./transformation/utils/safe-names"; -import { EmitHost } from "./transpilation"; +import { EmitHost, getEmitPath } from "./transpilation"; import { intersperse, normalizeSlashes } from "./utils"; // https://www.lua.org/pil/2.4.html @@ -239,12 +238,17 @@ export class LuaPrinter { luaLibImport === LuaLibImportKind.Always || (luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0) ) { - // Require lualib bundle - header += 'require("lualib_bundle");\n'; + // Import lualib features + const importStatements = loadImportedLualibFeatures( + file.luaLibFeatures, + this.emitHost, + luaLibImport === LuaLibImportKind.Always + ); + header += this.concatNodes(...this.printStatementArray(importStatements)).toString(); } else if (luaLibImport === LuaLibImportKind.Inline && file.luaLibFeatures.size > 0) { // Inline lualib features header += "-- Lua Library inline imports\n"; - header += loadLuaLibFeatures(file.luaLibFeatures, this.emitHost); + header += loadInlineLualibFeatures(file.luaLibFeatures, this.emitHost); } if (this.options.sourceMapTraceback && !isBundleEnabled(this.options)) { diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 37e809eb6..4abb02fd8 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -46,6 +46,11 @@ export const optionDeclarations: CommandLineOption[] = [ type: "enum", choices: Object.values(LuaLibImportKind), }, + { + name: "luaLibProject", + description: "If this project is lualib source.", + type: "boolean", + }, { name: "luaTarget", aliases: ["lt"], diff --git a/src/lualib/ArrayConcat.ts b/src/lualib/ArrayConcat.ts index 30da83514..56f3d1b5f 100644 --- a/src/lualib/ArrayConcat.ts +++ b/src/lualib/ArrayConcat.ts @@ -1,4 +1,4 @@ -function __TS__ArrayConcat(this: void, arr1: any[], ...args: any[]): any[] { +export function __TS__ArrayConcat(this: void, arr1: any[], ...args: any[]): any[] { const out: any[] = []; for (const val of arr1) { out[out.length] = val; diff --git a/src/lualib/ArrayEntries.ts b/src/lualib/ArrayEntries.ts index 7d12f1347..6cdef13a3 100644 --- a/src/lualib/ArrayEntries.ts +++ b/src/lualib/ArrayEntries.ts @@ -1,5 +1,5 @@ // https://262.ecma-international.org/10.0/#sec-array.prototype.entries -function __TS__ArrayEntries(this: void, array: T[]): IterableIterator<[number, T]> { +export function __TS__ArrayEntries(this: void, array: T[]): IterableIterator<[number, T]> { let key = 0; return { [Symbol.iterator](): IterableIterator<[number, T]> { diff --git a/src/lualib/ArrayEvery.ts b/src/lualib/ArrayEvery.ts index 011dd9abe..43dd312ea 100644 --- a/src/lualib/ArrayEvery.ts +++ b/src/lualib/ArrayEvery.ts @@ -1,4 +1,4 @@ -function __TS__ArrayEvery( +export function __TS__ArrayEvery( this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean diff --git a/src/lualib/ArrayFilter.ts b/src/lualib/ArrayFilter.ts index 3d88ddffa..0559a02d6 100644 --- a/src/lualib/ArrayFilter.ts +++ b/src/lualib/ArrayFilter.ts @@ -1,4 +1,4 @@ -function __TS__ArrayFilter( +export function __TS__ArrayFilter( this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean diff --git a/src/lualib/ArrayFind.ts b/src/lualib/ArrayFind.ts index 608bd3ef6..a9a0efc70 100644 --- a/src/lualib/ArrayFind.ts +++ b/src/lualib/ArrayFind.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-array.prototype.find -function __TS__ArrayFind( +export function __TS__ArrayFind( this: void, arr: T[], predicate: (value: T, index: number, obj: T[]) => unknown diff --git a/src/lualib/ArrayFindIndex.ts b/src/lualib/ArrayFindIndex.ts index 53d83cb39..945fed18d 100644 --- a/src/lualib/ArrayFindIndex.ts +++ b/src/lualib/ArrayFindIndex.ts @@ -1,4 +1,4 @@ -function __TS__ArrayFindIndex( +export function __TS__ArrayFindIndex( this: void, arr: T[], callbackFn: (element: T, index?: number, array?: T[]) => boolean diff --git a/src/lualib/ArrayFlat.ts b/src/lualib/ArrayFlat.ts index 2bed314cb..b98861aeb 100644 --- a/src/lualib/ArrayFlat.ts +++ b/src/lualib/ArrayFlat.ts @@ -1,4 +1,4 @@ -function __TS__ArrayFlat(this: void, array: any[], depth = 1): any[] { +export function __TS__ArrayFlat(this: void, array: any[], depth = 1): any[] { let result: any[] = []; for (const value of array) { if (depth > 0 && Array.isArray(value)) { diff --git a/src/lualib/ArrayFlatMap.ts b/src/lualib/ArrayFlatMap.ts index c31af9046..29f92c1f5 100644 --- a/src/lualib/ArrayFlatMap.ts +++ b/src/lualib/ArrayFlatMap.ts @@ -1,4 +1,4 @@ -function __TS__ArrayFlatMap( +export function __TS__ArrayFlatMap( this: void, array: T[], callback: (value: T, index: number, array: T[]) => U | readonly U[] diff --git a/src/lualib/ArrayForEach.ts b/src/lualib/ArrayForEach.ts index 96ac4a806..37a610433 100644 --- a/src/lualib/ArrayForEach.ts +++ b/src/lualib/ArrayForEach.ts @@ -1,4 +1,4 @@ -function __TS__ArrayForEach( +export function __TS__ArrayForEach( this: void, arr: T[], callbackFn: (value: T, index?: number, array?: any[]) => any diff --git a/src/lualib/ArrayIncludes.ts b/src/lualib/ArrayIncludes.ts index b012f87bf..88e788a15 100644 --- a/src/lualib/ArrayIncludes.ts +++ b/src/lualib/ArrayIncludes.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.includes -function __TS__ArrayIncludes(this: T[], searchElement: T, fromIndex = 0): boolean { +export function __TS__ArrayIncludes(this: T[], searchElement: T, fromIndex = 0): boolean { const len = this.length; let k = fromIndex; diff --git a/src/lualib/ArrayIndexOf.ts b/src/lualib/ArrayIndexOf.ts index e91a1a5dc..1631caf76 100644 --- a/src/lualib/ArrayIndexOf.ts +++ b/src/lualib/ArrayIndexOf.ts @@ -1,4 +1,4 @@ -function __TS__ArrayIndexOf(this: void, arr: T[], searchElement: T, fromIndex?: number): number { +export function __TS__ArrayIndexOf(this: void, arr: T[], searchElement: T, fromIndex?: number): number { const len = arr.length; if (len === 0) { return -1; diff --git a/src/lualib/ArrayIsArray.ts b/src/lualib/ArrayIsArray.ts index 417ae6e6e..7d2ec3112 100644 --- a/src/lualib/ArrayIsArray.ts +++ b/src/lualib/ArrayIsArray.ts @@ -1,6 +1,6 @@ declare type NextEmptyCheck = (this: void, table: any, index: undefined) => unknown | undefined; -function __TS__ArrayIsArray(this: void, value: any): value is any[] { +export function __TS__ArrayIsArray(this: void, value: any): value is any[] { // Workaround to determine if value is an array or not (fails in case of objects without keys) // See discussion in: https://github.com/TypeScriptToLua/TypeScriptToLua/pull/737 return type(value) === "table" && (1 in value || (next as NextEmptyCheck)(value, undefined) === undefined); diff --git a/src/lualib/ArrayJoin.ts b/src/lualib/ArrayJoin.ts index 33e220181..7400b886e 100644 --- a/src/lualib/ArrayJoin.ts +++ b/src/lualib/ArrayJoin.ts @@ -1,4 +1,4 @@ -function __TS__ArrayJoin(this: unknown[], separator = ",") { +export function __TS__ArrayJoin(this: unknown[], separator = ",") { let result = ""; for (const [index, value] of ipairs(this)) { if (index > 1) result += separator; diff --git a/src/lualib/ArrayMap.ts b/src/lualib/ArrayMap.ts index 04ee22a48..7e9c868fa 100644 --- a/src/lualib/ArrayMap.ts +++ b/src/lualib/ArrayMap.ts @@ -1,4 +1,8 @@ -function __TS__ArrayMap(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] { +export function __TS__ArrayMap( + this: void, + arr: T[], + callbackfn: (value: T, index?: number, array?: T[]) => U +): U[] { const newArray: U[] = []; for (let i = 0; i < arr.length; i++) { newArray[i] = callbackfn(arr[i], i, arr); diff --git a/src/lualib/ArrayPush.ts b/src/lualib/ArrayPush.ts index f09005e44..39d4f241e 100644 --- a/src/lualib/ArrayPush.ts +++ b/src/lualib/ArrayPush.ts @@ -1,4 +1,4 @@ -function __TS__ArrayPush(this: void, arr: T[], ...items: T[]): number { +export function __TS__ArrayPush(this: void, arr: T[], ...items: T[]): number { for (const item of items) { arr[arr.length] = item; } diff --git a/src/lualib/ArrayReduce.ts b/src/lualib/ArrayReduce.ts index 5ac7bd856..737128cd0 100644 --- a/src/lualib/ArrayReduce.ts +++ b/src/lualib/ArrayReduce.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.reduce -function __TS__ArrayReduce( +export function __TS__ArrayReduce( this: void, arr: TElement[], callbackFn: (accumulator: TAccumulator, currentValue: TElement, index: number, array: TElement[]) => TAccumulator, diff --git a/src/lualib/ArrayReduceRight.ts b/src/lualib/ArrayReduceRight.ts index 26258b862..91099946a 100644 --- a/src/lualib/ArrayReduceRight.ts +++ b/src/lualib/ArrayReduceRight.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.reduce -function __TS__ArrayReduceRight( +export function __TS__ArrayReduceRight( this: void, arr: TElement[], callbackFn: (accumulator: TAccumulator, currentValue: TElement, index: number, array: TElement[]) => TAccumulator, diff --git a/src/lualib/ArrayReverse.ts b/src/lualib/ArrayReverse.ts index b4a97a2c1..e644355d9 100644 --- a/src/lualib/ArrayReverse.ts +++ b/src/lualib/ArrayReverse.ts @@ -1,4 +1,4 @@ -function __TS__ArrayReverse(this: void, arr: any[]): any[] { +export function __TS__ArrayReverse(this: void, arr: any[]): any[] { let i = 0; let j = arr.length - 1; while (i < j) { diff --git a/src/lualib/ArraySetLength.ts b/src/lualib/ArraySetLength.ts index e1bd8ea03..6fde3254d 100644 --- a/src/lualib/ArraySetLength.ts +++ b/src/lualib/ArraySetLength.ts @@ -1,4 +1,4 @@ -function __TS__ArraySetLength(this: void, arr: T[], length: number): number { +export function __TS__ArraySetLength(this: void, arr: T[], length: number): number { if ( length < 0 || length !== length || // NaN diff --git a/src/lualib/ArrayShift.ts b/src/lualib/ArrayShift.ts index e39d9616b..799463d9d 100644 --- a/src/lualib/ArrayShift.ts +++ b/src/lualib/ArrayShift.ts @@ -1,3 +1,3 @@ -function __TS__ArrayShift(this: void, arr: T[]): T { +export function __TS__ArrayShift(this: void, arr: T[]): T { return table.remove(arr, 1); } diff --git a/src/lualib/ArraySlice.ts b/src/lualib/ArraySlice.ts index d38d96006..e9797c43f 100644 --- a/src/lualib/ArraySlice.ts +++ b/src/lualib/ArraySlice.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.slice -function __TS__ArraySlice(this: void, list: T[], first: number, last: number): T[] { +export function __TS__ArraySlice(this: void, list: T[], first: number, last: number): T[] { const len = list.length; const relativeStart = first || 0; diff --git a/src/lualib/ArraySome.ts b/src/lualib/ArraySome.ts index 9122b1bbc..1c4725d4c 100644 --- a/src/lualib/ArraySome.ts +++ b/src/lualib/ArraySome.ts @@ -1,4 +1,4 @@ -function __TS__ArraySome( +export function __TS__ArraySome( this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean diff --git a/src/lualib/ArraySort.ts b/src/lualib/ArraySort.ts index 1855c3bb5..034344f76 100644 --- a/src/lualib/ArraySort.ts +++ b/src/lualib/ArraySort.ts @@ -1,4 +1,4 @@ -function __TS__ArraySort(this: void, arr: T[], compareFn?: (a: T, b: T) => number): T[] { +export function __TS__ArraySort(this: void, arr: T[], compareFn?: (a: T, b: T) => number): T[] { if (compareFn !== undefined) { table.sort(arr, (a, b) => compareFn(a, b) < 0); } else { diff --git a/src/lualib/ArraySplice.ts b/src/lualib/ArraySplice.ts index d4a122181..273525ed0 100644 --- a/src/lualib/ArraySplice.ts +++ b/src/lualib/ArraySplice.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.splice -function __TS__ArraySplice(this: void, list: T[], ...args: T[]): T[] { +export function __TS__ArraySplice(this: void, list: T[], ...args: T[]): T[] { const len = list.length; const actualArgumentCount = select("#", ...args); diff --git a/src/lualib/ArrayToObject.ts b/src/lualib/ArrayToObject.ts index 460a77250..e3ed41f78 100644 --- a/src/lualib/ArrayToObject.ts +++ b/src/lualib/ArrayToObject.ts @@ -1,4 +1,4 @@ -function __TS__ArrayToObject(this: void, array: T[]): Record { +export function __TS__ArrayToObject(this: void, array: T[]): Record { const object: Record = {}; for (let i = 0; i < array.length; i += 1) { object[i] = array[i]; diff --git a/src/lualib/ArrayUnshift.ts b/src/lualib/ArrayUnshift.ts index 95dc15b8f..c3dd6bdb4 100644 --- a/src/lualib/ArrayUnshift.ts +++ b/src/lualib/ArrayUnshift.ts @@ -1,4 +1,4 @@ -function __TS__ArrayUnshift(this: void, arr: T[], ...items: T[]): number { +export function __TS__ArrayUnshift(this: void, arr: T[], ...items: T[]): number { for (let i = items.length - 1; i >= 0; --i) { table.insert(arr, 1, items[i]); } diff --git a/src/lualib/Await.ts b/src/lualib/Await.ts index a3139d88e..acb9607e4 100644 --- a/src/lualib/Await.ts +++ b/src/lualib/Await.ts @@ -14,10 +14,12 @@ // }; // +import { __TS__Promise } from "./Promise"; + type ErrorHandler = (this: void, error: unknown) => unknown; // eslint-disable-next-line @typescript-eslint/promise-function-async -function __TS__AsyncAwaiter(this: void, generator: (this: void) => void) { +export function __TS__AsyncAwaiter(this: void, generator: (this: void) => void) { return new Promise((resolve, reject) => { const asyncCoroutine = coroutine.create(generator); @@ -70,6 +72,6 @@ function __TS__AsyncAwaiter(this: void, generator: (this: void) => void) { }); } -function __TS__Await(this: void, errorHandler: ErrorHandler, thing: unknown) { +export function __TS__Await(this: void, errorHandler: ErrorHandler, thing: unknown) { return coroutine.yield(errorHandler, thing); } diff --git a/src/lualib/Class.ts b/src/lualib/Class.ts index d5d7af93d..bdcd7ce4f 100644 --- a/src/lualib/Class.ts +++ b/src/lualib/Class.ts @@ -1,4 +1,4 @@ -function __TS__Class(): LuaClass { +export function __TS__Class(): LuaClass { const c: LuaClass = { prototype: {} }; c.prototype.__index = c.prototype; c.prototype.constructor = c; diff --git a/src/lualib/ClassExtends.ts b/src/lualib/ClassExtends.ts index 0df851629..6075868d7 100644 --- a/src/lualib/ClassExtends.ts +++ b/src/lualib/ClassExtends.ts @@ -1,4 +1,4 @@ -function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void { +export function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void { target.____super = base; // Set base class as a metatable, because descriptors use `getmetatable` to get extended prototype diff --git a/src/lualib/CloneDescriptor.ts b/src/lualib/CloneDescriptor.ts index d74292bab..2afe76914 100644 --- a/src/lualib/CloneDescriptor.ts +++ b/src/lualib/CloneDescriptor.ts @@ -1,4 +1,4 @@ -function __TS__CloneDescriptor( +export function __TS__CloneDescriptor( this: void, { enumerable, configurable, get, set, writable, value }: PropertyDescriptor ): PropertyDescriptor { diff --git a/src/lualib/Decorate.ts b/src/lualib/Decorate.ts index 816721da7..50510f12c 100644 --- a/src/lualib/Decorate.ts +++ b/src/lualib/Decorate.ts @@ -1,12 +1,11 @@ /** * SEE: https://github.com/Microsoft/TypeScript/blob/master/src/compiler/transformers/ts.ts#L3598 */ -type Decorator = ( - target: TTarget, - key?: TKey, - descriptor?: PropertyDescriptor -) => TTarget; -function __TS__Decorate( +import { __TS__ObjectGetOwnPropertyDescriptor } from "./ObjectGetOwnPropertyDescriptor"; +import { __TS__SetDescriptor } from "./SetDescriptor"; +import { Decorator } from "./Decorator"; + +export function __TS__Decorate( this: void, decorators: Array>, target: TTarget, diff --git a/src/lualib/DecorateParam.ts b/src/lualib/DecorateParam.ts index 4e11f3e54..f6c405015 100644 --- a/src/lualib/DecorateParam.ts +++ b/src/lualib/DecorateParam.ts @@ -1,9 +1,11 @@ +import { Decorator } from "./Decorator"; + type ParamDecorator = ( target: TTarget, key: TKey, index: number ) => TTarget; -function __TS__DecorateParam( +export function __TS__DecorateParam( this: void, paramIndex: number, decorator: ParamDecorator diff --git a/src/lualib/Decorator.d.ts b/src/lualib/Decorator.d.ts new file mode 100644 index 000000000..338b11b3f --- /dev/null +++ b/src/lualib/Decorator.d.ts @@ -0,0 +1,5 @@ +export type Decorator = ( + target: TTarget, + key?: TKey, + descriptor?: PropertyDescriptor +) => TTarget; diff --git a/src/lualib/DelegatedYield.ts b/src/lualib/DelegatedYield.ts index ab47d02d8..827dd6326 100644 --- a/src/lualib/DelegatedYield.ts +++ b/src/lualib/DelegatedYield.ts @@ -1,4 +1,6 @@ -function __TS__DelegatedYield(this: void, iterable: string | GeneratorIterator | Iterable | readonly T[]) { +import { GeneratorIterator } from "./GeneratorIterator"; + +export function __TS__DelegatedYield(this: void, iterable: string | GeneratorIterator | Iterable | readonly T[]) { if (typeof iterable === "string") { for (const index of $range(0, iterable.length - 1)) { coroutine.yield(iterable[index]); diff --git a/src/lualib/Delete.ts b/src/lualib/Delete.ts index 8dfad76ad..07499f44f 100644 --- a/src/lualib/Delete.ts +++ b/src/lualib/Delete.ts @@ -1,4 +1,6 @@ -function __TS__Delete(this: void, target: any, key: any): boolean { +import { __TS__ObjectGetOwnPropertyDescriptors } from "./ObjectGetOwnPropertyDescriptors"; + +export function __TS__Delete(this: void, target: any, key: any): boolean { const descriptors = __TS__ObjectGetOwnPropertyDescriptors(target); const descriptor = descriptors[key]; if (descriptor) { diff --git a/src/lualib/Error.ts b/src/lualib/Error.ts index 197ff816b..2ebb2aaaa 100644 --- a/src/lualib/Error.ts +++ b/src/lualib/Error.ts @@ -3,7 +3,7 @@ interface ErrorType { new (...args: any[]): Error; } -function __TS__GetErrorStack(constructor: () => any): string { +function getErrorStack(constructor: () => any): string { let level = 1; while (true) { const info = debug.getinfo(level, "f"); @@ -20,7 +20,7 @@ function __TS__GetErrorStack(constructor: () => any): string { return debug.traceback(undefined, level); } -function __TS__WrapErrorToString(getDescription: (this: T) => string): (this: T) => string { +function wrapErrorToString(getDescription: (this: T) => string): (this: T) => string { return function (this: Error): string { const description = getDescription.call(this); const caller = debug.getinfo(3, "f"); @@ -32,24 +32,24 @@ function __TS__WrapErrorToString(getDescription: (this: T) => s }; } -function __TS__InitErrorClass(Type: ErrorType, name: string): any { +function initErrorClass(Type: ErrorType, name: string): any { Type.name = name; return setmetatable(Type, { __call: (_self: any, message: string) => new Type(message), }); } -Error = __TS__InitErrorClass( +export const Error: ErrorConstructor = initErrorClass( class implements Error { public name = "Error"; public stack: string; constructor(public message = "") { - this.stack = __TS__GetErrorStack((this.constructor as any).new); + this.stack = getErrorStack((this.constructor as any).new); const metatable = getmetatable(this); if (!metatable.__errorToStringPatched) { metatable.__errorToStringPatched = true; - metatable.__tostring = __TS__WrapErrorToString(metatable.__tostring); + metatable.__tostring = wrapErrorToString(metatable.__tostring); } } @@ -60,11 +60,17 @@ Error = __TS__InitErrorClass( "Error" ); -for (const errorName of ["RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) { - globalThis[errorName] = __TS__InitErrorClass( +function createErrorClass(name: string) { + return initErrorClass( class extends Error { - public name = errorName; + public name = name; }, - errorName + name ); } + +export const RangeError = createErrorClass("RangeError"); +export const ReferenceError = createErrorClass("ReferenceError"); +export const SyntaxError = createErrorClass("SyntaxError"); +export const TypeError = createErrorClass("TypeError"); +export const URIError = createErrorClass("URIError"); diff --git a/src/lualib/FunctionBind.ts b/src/lualib/FunctionBind.ts index e12fbe37c..a6cb2fac5 100644 --- a/src/lualib/FunctionBind.ts +++ b/src/lualib/FunctionBind.ts @@ -1,4 +1,4 @@ -function __TS__FunctionBind( +export function __TS__FunctionBind( this: void, fn: (this: void, ...argArray: any[]) => any, thisArg: any, diff --git a/src/lualib/Generator.ts b/src/lualib/Generator.ts index 1e18f2e25..0122602a2 100644 --- a/src/lualib/Generator.ts +++ b/src/lualib/Generator.ts @@ -1,14 +1,10 @@ -interface GeneratorIterator { - ____coroutine: LuaThread; - [Symbol.iterator](): GeneratorIterator; - next: typeof __TS__GeneratorNext; -} +import { GeneratorIterator } from "./GeneratorIterator"; -function __TS__GeneratorIterator(this: GeneratorIterator) { +function generatorIterator(this: GeneratorIterator) { return this; } -function __TS__GeneratorNext(this: GeneratorIterator, ...args: any[]) { +function generatorNext(this: GeneratorIterator, ...args: any[]) { const co = this.____coroutine; if (coroutine.status(co) === "dead") return { done: true }; @@ -18,14 +14,14 @@ function __TS__GeneratorNext(this: GeneratorIterator, ...args: any[]) { return { value, done: coroutine.status(co) === "dead" }; } -function __TS__Generator(this: void, fn: (this: void, ...args: any[]) => any) { +export function __TS__Generator(this: void, fn: (this: void, ...args: any[]) => any) { return function (this: void, ...args: any[]): GeneratorIterator { const argsLength = select("#", ...args); return { // Using explicit this there, since we don't pass arguments after the first nil and context is likely to be nil ____coroutine: coroutine.create(() => fn(...(unpack ?? table.unpack)(args, 1, argsLength))), - [Symbol.iterator]: __TS__GeneratorIterator, - next: __TS__GeneratorNext, + [Symbol.iterator]: generatorIterator, + next: generatorNext, }; }; } diff --git a/src/lualib/GeneratorIterator.d.ts b/src/lualib/GeneratorIterator.d.ts new file mode 100644 index 000000000..2509046b5 --- /dev/null +++ b/src/lualib/GeneratorIterator.d.ts @@ -0,0 +1,5 @@ +export interface GeneratorIterator { + ____coroutine: LuaThread; + [Symbol.iterator](): GeneratorIterator; + next: typeof generatorNext; +} diff --git a/src/lualib/InstanceOf.ts b/src/lualib/InstanceOf.ts index a4d80c881..a469cc35f 100644 --- a/src/lualib/InstanceOf.ts +++ b/src/lualib/InstanceOf.ts @@ -1,4 +1,4 @@ -function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass): boolean { +export function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass): boolean { if (typeof classTbl !== "object") { throw "Right-hand side of 'instanceof' is not an object"; } diff --git a/src/lualib/InstanceOfObject.ts b/src/lualib/InstanceOfObject.ts index 681e243c5..28e32150e 100644 --- a/src/lualib/InstanceOfObject.ts +++ b/src/lualib/InstanceOfObject.ts @@ -1,4 +1,4 @@ -function __TS__InstanceOfObject(this: void, value: unknown): boolean { +export function __TS__InstanceOfObject(this: void, value: unknown): boolean { const valueType = type(value); return valueType === "table" || valueType === "function"; } diff --git a/src/lualib/Iterator.ts b/src/lualib/Iterator.ts index 29da08683..6991ea888 100644 --- a/src/lualib/Iterator.ts +++ b/src/lualib/Iterator.ts @@ -1,4 +1,6 @@ -function __TS__IteratorGeneratorStep(this: GeneratorIterator): LuaMultiReturn<[true, any] | []> { +import { GeneratorIterator } from "./GeneratorIterator"; + +function iteratorGeneratorStep(this: GeneratorIterator): LuaMultiReturn<[true, any] | []> { const co = this.____coroutine; const [status, value] = coroutine.resume(co); @@ -8,29 +10,29 @@ function __TS__IteratorGeneratorStep(this: GeneratorIterator): LuaMultiReturn<[t return $multi(true, value); } -function __TS__IteratorIteratorStep(this: Iterator): LuaMultiReturn<[true, T] | []> { +function iteratorIteratorStep(this: Iterator): LuaMultiReturn<[true, T] | []> { const result = this.next(); if (result.done) return $multi(); return $multi(true, result.value); } -function __TS__IteratorStringStep(this: string, index: number): LuaMultiReturn<[number, string] | []> { +function iteratorStringStep(this: string, index: number): LuaMultiReturn<[number, string] | []> { index += 1; if (index > this.length) return $multi(); return $multi(index, string.sub(this, index, index)); } -function __TS__Iterator( +export function __TS__Iterator( this: void, iterable: string | GeneratorIterator | Iterable | readonly T[] ): LuaMultiReturn<[(...args: any[]) => [any, any] | [], ...any[]]> | LuaIterable> { if (typeof iterable === "string") { - return $multi(__TS__IteratorStringStep, iterable, 0); + return $multi(iteratorStringStep, iterable, 0); } else if ("____coroutine" in iterable) { - return $multi(__TS__IteratorGeneratorStep, iterable); + return $multi(iteratorGeneratorStep, iterable); } else if (iterable[Symbol.iterator]) { const iterator = iterable[Symbol.iterator](); - return $multi(__TS__IteratorIteratorStep, iterator); + return $multi(iteratorIteratorStep, iterator); } else { return ipairs(iterable as readonly T[]); } diff --git a/src/lualib/Map.ts b/src/lualib/Map.ts index e442b6132..fb1382fc3 100644 --- a/src/lualib/Map.ts +++ b/src/lualib/Map.ts @@ -1,4 +1,4 @@ -Map = class Map { +export class Map { public static [Symbol.species] = Map; public [Symbol.toStringTag] = "Map"; @@ -156,4 +156,4 @@ Map = class Map { }, }; } -}; +} diff --git a/src/lualib/MathAtan2.ts b/src/lualib/MathAtan2.ts index fdf322664..185cfe678 100644 --- a/src/lualib/MathAtan2.ts +++ b/src/lualib/MathAtan2.ts @@ -1 +1 @@ -const __TS__MathAtan2 = math.atan2 || math.atan; +export const __TS__MathAtan2 = math.atan2 || math.atan; diff --git a/src/lualib/MathSign.ts b/src/lualib/MathSign.ts index e94614c83..f3be69e8d 100644 --- a/src/lualib/MathSign.ts +++ b/src/lualib/MathSign.ts @@ -1,4 +1,4 @@ -function __TS__MathSign(this: void, val: number) { +export function __TS__MathSign(this: void, val: number) { if (val > 0) { return 1; } else if (val < 0) { diff --git a/src/lualib/New.ts b/src/lualib/New.ts index 8b33ec837..b54890917 100644 --- a/src/lualib/New.ts +++ b/src/lualib/New.ts @@ -1,4 +1,4 @@ -function __TS__New(this: void, target: LuaClass, ...args: any[]): any { +export function __TS__New(this: void, target: LuaClass, ...args: any[]): any { const instance: any = setmetatable({}, target.prototype); instance.____constructor(...args); return instance; diff --git a/src/lualib/Number.ts b/src/lualib/Number.ts index 0b19a1600..b258b48f6 100644 --- a/src/lualib/Number.ts +++ b/src/lualib/Number.ts @@ -1,4 +1,4 @@ -function __TS__Number(this: void, value: unknown): number { +export function __TS__Number(this: void, value: unknown): number { const valueType = type(value); if (valueType === "number") { return value as number; diff --git a/src/lualib/NumberIsFinite.ts b/src/lualib/NumberIsFinite.ts index 98df30790..f225b8c5c 100644 --- a/src/lualib/NumberIsFinite.ts +++ b/src/lualib/NumberIsFinite.ts @@ -1,3 +1,3 @@ -function __TS__NumberIsFinite(this: void, value: unknown): boolean { +export function __TS__NumberIsFinite(this: void, value: unknown): boolean { return typeof value === "number" && value === value && value !== Infinity && value !== -Infinity; } diff --git a/src/lualib/NumberIsNaN.ts b/src/lualib/NumberIsNaN.ts index b040f5e5b..e57631cc1 100644 --- a/src/lualib/NumberIsNaN.ts +++ b/src/lualib/NumberIsNaN.ts @@ -1,3 +1,3 @@ -function __TS__NumberIsNaN(this: void, value: unknown): boolean { +export function __TS__NumberIsNaN(this: void, value: unknown): boolean { return value !== value; } diff --git a/src/lualib/NumberToString.ts b/src/lualib/NumberToString.ts index 52da58d7c..1e1a1ad03 100644 --- a/src/lualib/NumberToString.ts +++ b/src/lualib/NumberToString.ts @@ -1,7 +1,7 @@ -const ____radixChars = "0123456789abcdefghijklmnopqrstuvwxyz"; +const radixChars = "0123456789abcdefghijklmnopqrstuvwxyz"; // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-number.prototype.tostring -function __TS__NumberToString(this: number, radix?: number): string { +export function __TS__NumberToString(this: number, radix?: number): string { if (radix === undefined || radix === 10 || this === Infinity || this === -Infinity || this !== this) { return this.toString(); } @@ -20,7 +20,7 @@ function __TS__NumberToString(this: number, radix?: number): string { result = string.format("%x", integer); } else { do { - result = ____radixChars[integer % radix] + result; + result = radixChars[integer % radix] + result; integer = Math.floor(integer / radix); } while (integer !== 0); } @@ -33,7 +33,7 @@ function __TS__NumberToString(this: number, radix?: number): string { fraction *= radix; delta *= radix; const digit = Math.floor(fraction); - result += ____radixChars[digit]; + result += radixChars[digit]; fraction -= digit; // TODO: Round to even } while (fraction >= delta); diff --git a/src/lualib/ObjectAssign.ts b/src/lualib/ObjectAssign.ts index bcf521c07..a351c030a 100644 --- a/src/lualib/ObjectAssign.ts +++ b/src/lualib/ObjectAssign.ts @@ -1,6 +1,6 @@ // https://tc39.github.io/ecma262/#sec-object.assign // eslint-disable-next-line @typescript-eslint/ban-types -function __TS__ObjectAssign(this: void, to: T, ...sources: object[]): T { +export function __TS__ObjectAssign(this: void, to: T, ...sources: object[]): T { if (to === undefined) { return to; } diff --git a/src/lualib/ObjectDefineProperty.ts b/src/lualib/ObjectDefineProperty.ts index f37b36c2a..6b275bfa0 100644 --- a/src/lualib/ObjectDefineProperty.ts +++ b/src/lualib/ObjectDefineProperty.ts @@ -1,7 +1,14 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty // eslint-disable-next-line @typescript-eslint/ban-types -function __TS__ObjectDefineProperty(this: void, target: T, key: any, desc: PropertyDescriptor): T { +import { __TS__SetDescriptor } from "./SetDescriptor"; + +export function __TS__ObjectDefineProperty( + this: void, + target: T, + key: any, + desc: PropertyDescriptor +): T { const luaKey = typeof key === "number" ? key + 1 : key; const value = rawget(target, luaKey); diff --git a/src/lualib/ObjectEntries.ts b/src/lualib/ObjectEntries.ts index f491bd039..3059fbecf 100644 --- a/src/lualib/ObjectEntries.ts +++ b/src/lualib/ObjectEntries.ts @@ -1,4 +1,4 @@ -function __TS__ObjectEntries(this: void, obj: any): Array { +export function __TS__ObjectEntries(this: void, obj: any): Array { const result = []; for (const key in obj) { result[result.length] = [key, obj[key]]; diff --git a/src/lualib/ObjectFromEntries.ts b/src/lualib/ObjectFromEntries.ts index efdf7dd90..88c224cf7 100644 --- a/src/lualib/ObjectFromEntries.ts +++ b/src/lualib/ObjectFromEntries.ts @@ -1,4 +1,4 @@ -function __TS__ObjectFromEntries( +export function __TS__ObjectFromEntries( this: void, entries: ReadonlyArray<[string, T]> | Iterable<[string, T]> ): Record { diff --git a/src/lualib/ObjectGetOwnPropertyDescriptor.ts b/src/lualib/ObjectGetOwnPropertyDescriptor.ts index 84dab33df..1f7e660bf 100644 --- a/src/lualib/ObjectGetOwnPropertyDescriptor.ts +++ b/src/lualib/ObjectGetOwnPropertyDescriptor.ts @@ -1,4 +1,8 @@ -function __TS__ObjectGetOwnPropertyDescriptor(this: void, object: any, key: any): PropertyDescriptor | undefined { +export function __TS__ObjectGetOwnPropertyDescriptor( + this: void, + object: any, + key: any +): PropertyDescriptor | undefined { const metatable = getmetatable(object); if (!metatable) return; if (!rawget(metatable, "_descriptors")) return; diff --git a/src/lualib/ObjectGetOwnPropertyDescriptors.ts b/src/lualib/ObjectGetOwnPropertyDescriptors.ts index 04aee8b51..41d92ddfc 100644 --- a/src/lualib/ObjectGetOwnPropertyDescriptors.ts +++ b/src/lualib/ObjectGetOwnPropertyDescriptors.ts @@ -1,4 +1,7 @@ -function __TS__ObjectGetOwnPropertyDescriptors(this: void, object: any): Record { +export function __TS__ObjectGetOwnPropertyDescriptors( + this: void, + object: any +): Record { const metatable = getmetatable(object); if (!metatable) return {}; return rawget(metatable, "_descriptors") || {}; diff --git a/src/lualib/ObjectKeys.ts b/src/lualib/ObjectKeys.ts index 8163a620a..d07f6c8d6 100644 --- a/src/lualib/ObjectKeys.ts +++ b/src/lualib/ObjectKeys.ts @@ -1,4 +1,4 @@ -function __TS__ObjectKeys(this: void, obj: any): Array { +export function __TS__ObjectKeys(this: void, obj: any): Array { const result = []; for (const key in obj) { result[result.length] = key; diff --git a/src/lualib/ObjectRest.ts b/src/lualib/ObjectRest.ts index 7da14b2fb..276135ce5 100644 --- a/src/lualib/ObjectRest.ts +++ b/src/lualib/ObjectRest.ts @@ -1,4 +1,4 @@ -function __TS__ObjectRest( +export function __TS__ObjectRest( this: void, target: Record, usedProperties: Partial> diff --git a/src/lualib/ObjectValues.ts b/src/lualib/ObjectValues.ts index e850f6a31..02ab400cb 100644 --- a/src/lualib/ObjectValues.ts +++ b/src/lualib/ObjectValues.ts @@ -1,4 +1,4 @@ -function __TS__ObjectValues(this: void, obj: any): Array { +export function __TS__ObjectValues(this: void, obj: any): Array { const result = []; for (const key in obj) { result[result.length] = obj[key]; diff --git a/src/lualib/ParseFloat.ts b/src/lualib/ParseFloat.ts index 0c6d04d65..94ca97429 100644 --- a/src/lualib/ParseFloat.ts +++ b/src/lualib/ParseFloat.ts @@ -1,4 +1,4 @@ -function __TS__ParseFloat(this: void, numberString: string): number { +export function __TS__ParseFloat(this: void, numberString: string): number { // Check if string is infinity const [infinityMatch] = string.match(numberString, "^%s*(-?Infinity)"); if (infinityMatch) { diff --git a/src/lualib/ParseInt.ts b/src/lualib/ParseInt.ts index cb1d851f4..208d84e44 100644 --- a/src/lualib/ParseInt.ts +++ b/src/lualib/ParseInt.ts @@ -1,6 +1,6 @@ -const __TS__parseInt_base_pattern = "0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTvVwWxXyYzZ"; +const parseIntBasePattern = "0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTvVwWxXyYzZ"; -function __TS__ParseInt(this: void, numberString: string, base?: number): number { +export function __TS__ParseInt(this: void, numberString: string, base?: number): number { // Check which base to use if none specified if (base === undefined) { base = 10; @@ -20,9 +20,7 @@ function __TS__ParseInt(this: void, numberString: string, base?: number): number // Calculate string match pattern to use const allowedDigits = - base <= 10 - ? __TS__parseInt_base_pattern.substring(0, base) - : __TS__parseInt_base_pattern.substr(0, 10 + 2 * (base - 10)); + base <= 10 ? parseIntBasePattern.substring(0, base) : parseIntBasePattern.substr(0, 10 + 2 * (base - 10)); const pattern = `^%s*(-?[${allowedDigits}]*)`; // Try to parse with Lua tonumber diff --git a/src/lualib/Promise.ts b/src/lualib/Promise.ts index de21de94a..bc44443ef 100644 --- a/src/lualib/Promise.ts +++ b/src/lualib/Promise.ts @@ -3,7 +3,7 @@ // Promises implemented based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise // and https://promisesaplus.com/ -enum __TS__PromiseState { +export enum __TS__PromiseState { Pending, Fulfilled, Rejected, @@ -12,7 +12,7 @@ enum __TS__PromiseState { type FulfillCallback = (value: TData) => TResult | PromiseLike; type RejectCallback = (reason: any) => TResult | PromiseLike; -function __TS__PromiseDeferred() { +function promiseDeferred() { let resolve: FulfillCallback; let reject: RejectCallback; const promise = new Promise((res, rej) => { @@ -23,11 +23,11 @@ function __TS__PromiseDeferred() { return { promise, resolve, reject }; } -function __TS__IsPromiseLike(thing: unknown): thing is PromiseLike { +function isPromiseLike(thing: unknown): thing is PromiseLike { return thing instanceof __TS__Promise; } -class __TS__Promise implements Promise { +export class __TS__Promise implements Promise { public state = __TS__PromiseState.Pending; public value?: T; public rejectionReason?: any; @@ -70,7 +70,7 @@ class __TS__Promise implements Promise { onFulfilled?: FulfillCallback, onRejected?: RejectCallback ): Promise { - const { promise, resolve, reject } = __TS__PromiseDeferred(); + const { promise, resolve, reject } = promiseDeferred(); const isFulfilled = this.state === __TS__PromiseState.Fulfilled; const isRejected = this.state === __TS__PromiseState.Rejected; @@ -110,10 +110,12 @@ class __TS__Promise implements Promise { return promise as Promise; } + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch public catch(onRejected?: (reason: any) => TResult | PromiseLike): Promise { return this.then(undefined, onRejected); } + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally public finally(onFinally?: () => void): Promise { if (onFinally) { @@ -179,12 +181,13 @@ class __TS__Promise implements Promise { } }; } + private handleCallbackData( data: TResult | PromiseLike, resolve: FulfillCallback, reject: RejectCallback ) { - if (__TS__IsPromiseLike(data)) { + if (isPromiseLike(data)) { const nextpromise = data as __TS__Promise; if (nextpromise.state === __TS__PromiseState.Fulfilled) { // If a handler function returns an already fulfilled promise, diff --git a/src/lualib/PromiseAll.ts b/src/lualib/PromiseAll.ts index e0d940977..5283ba49f 100644 --- a/src/lualib/PromiseAll.ts +++ b/src/lualib/PromiseAll.ts @@ -1,6 +1,8 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all +import { __TS__Promise, __TS__PromiseState } from "./Promise"; + // eslint-disable-next-line @typescript-eslint/promise-function-async -function __TS__PromiseAll(this: void, iterable: Iterable>): Promise { +export function __TS__PromiseAll(this: void, iterable: Iterable>): Promise { const results: T[] = []; const toResolve = new LuaTable>(); diff --git a/src/lualib/PromiseAllSettled.ts b/src/lualib/PromiseAllSettled.ts index 165e226f4..a872e646a 100644 --- a/src/lualib/PromiseAllSettled.ts +++ b/src/lualib/PromiseAllSettled.ts @@ -1,6 +1,8 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled +import { __TS__Promise, __TS__PromiseState } from "./Promise"; + // eslint-disable-next-line @typescript-eslint/promise-function-async -function __TS__PromiseAllSettled( +export function __TS__PromiseAllSettled( this: void, iterable: Iterable ): Promise ? U : T>>> { diff --git a/src/lualib/PromiseAny.ts b/src/lualib/PromiseAny.ts index eb70bf12f..7a368fe74 100644 --- a/src/lualib/PromiseAny.ts +++ b/src/lualib/PromiseAny.ts @@ -1,6 +1,8 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any +import { __TS__Promise, __TS__PromiseState } from "./Promise"; + // eslint-disable-next-line @typescript-eslint/promise-function-async -function __TS__PromiseAny(this: void, iterable: Iterable>): Promise { +export function __TS__PromiseAny(this: void, iterable: Iterable>): Promise { const rejections: string[] = []; const pending: Array> = []; diff --git a/src/lualib/PromiseRace.ts b/src/lualib/PromiseRace.ts index 3b3a1fb7b..d4f27f70f 100644 --- a/src/lualib/PromiseRace.ts +++ b/src/lualib/PromiseRace.ts @@ -1,6 +1,8 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race +import { __TS__PromiseState, __TS__Promise } from "./Promise"; + // eslint-disable-next-line @typescript-eslint/promise-function-async -function __TS__PromiseRace(this: void, iterable: Iterable>): Promise { +export function __TS__PromiseRace(this: void, iterable: Iterable>): Promise { const pending: Array> = []; for (const item of iterable) { diff --git a/src/lualib/Set.ts b/src/lualib/Set.ts index 71a8265ff..e4ac5e1cb 100644 --- a/src/lualib/Set.ts +++ b/src/lualib/Set.ts @@ -1,4 +1,4 @@ -Set = class Set { +export class Set { public static [Symbol.species] = Set; public [Symbol.toStringTag] = "Set"; @@ -145,4 +145,4 @@ Set = class Set { }, }; } -}; +} diff --git a/src/lualib/SetDescriptor.ts b/src/lualib/SetDescriptor.ts index 5659848d5..11c435162 100644 --- a/src/lualib/SetDescriptor.ts +++ b/src/lualib/SetDescriptor.ts @@ -1,4 +1,6 @@ -function ____descriptorIndex(this: any, key: string): void { +import { __TS__CloneDescriptor } from "./CloneDescriptor"; + +function descriptorIndex(this: any, key: string): void { const value = rawget(this, key); if (value !== null) { return value; @@ -27,7 +29,7 @@ function ____descriptorIndex(this: any, key: string): void { } } -function ____descriptorNewindex(this: any, key: string, value: any): void { +function descriptorNewIndex(this: any, key: string, value: any): void { let metatable = getmetatable(this); while (metatable) { const descriptors = rawget(metatable, "_descriptors"); @@ -54,7 +56,13 @@ function ____descriptorNewindex(this: any, key: string, value: any): void { } // It's also used directly in class transform to add descriptors to the prototype -function __TS__SetDescriptor(this: void, target: any, key: any, desc: PropertyDescriptor, isPrototype = false): void { +export function __TS__SetDescriptor( + this: void, + target: any, + key: any, + desc: PropertyDescriptor, + isPrototype = false +): void { let metatable = isPrototype ? target : getmetatable(target); if (!metatable) { metatable = {}; @@ -67,6 +75,6 @@ function __TS__SetDescriptor(this: void, target: any, key: any, desc: PropertyDe if (!rawget(metatable, "_descriptors")) metatable._descriptors = {}; const descriptor = __TS__CloneDescriptor(desc); metatable._descriptors[key] = descriptor; - metatable.__index = ____descriptorIndex; - metatable.__newindex = ____descriptorNewindex; + metatable.__index = descriptorIndex; + metatable.__newindex = descriptorNewIndex; } diff --git a/src/lualib/SourceMapTraceBack.ts b/src/lualib/SourceMapTraceBack.ts index 542a8c483..f33a0dcd4 100644 --- a/src/lualib/SourceMapTraceBack.ts +++ b/src/lualib/SourceMapTraceBack.ts @@ -7,18 +7,19 @@ interface SourceMap { declare function __TS__originalTraceback(this: void, thread?: LuaThread, message?: string, level?: number); -function __TS__SourceMapTraceBack(this: void, fileName: string, sourceMap: SourceMap): void { +export function __TS__SourceMapTraceBack(this: void, fileName: string, sourceMap: SourceMap): void { globalThis.__TS__sourcemap = globalThis.__TS__sourcemap || {}; globalThis.__TS__sourcemap[fileName] = sourceMap; if (globalThis.__TS__originalTraceback === undefined) { - globalThis.__TS__originalTraceback = debug.traceback; + const originalTraceback = debug.traceback; + globalThis.__TS__originalTraceback = originalTraceback; debug.traceback = ((thread, message, level) => { let trace: string; if (thread === undefined && message === undefined && level === undefined) { - trace = globalThis.__TS__originalTraceback(); + trace = originalTraceback(); } else { - trace = globalThis.__TS__originalTraceback(thread, message, level); + trace = originalTraceback(thread, message, level); } if (typeof trace !== "string") { diff --git a/src/lualib/SparseArray.d.ts b/src/lualib/SparseArray.d.ts new file mode 100644 index 000000000..768d93d7f --- /dev/null +++ b/src/lualib/SparseArray.d.ts @@ -0,0 +1 @@ +export type __TS__SparseArray = T[] & { sparseLength: number }; diff --git a/src/lualib/SparseArrayNew.ts b/src/lualib/SparseArrayNew.ts index 2370871ca..07bfae13e 100644 --- a/src/lualib/SparseArrayNew.ts +++ b/src/lualib/SparseArrayNew.ts @@ -1,6 +1,6 @@ -type __TS__SparseArray = T[] & { sparseLength: number }; +import { __TS__SparseArray } from "./SparseArray"; -function __TS__SparseArrayNew(this: void, ...args: T[]): __TS__SparseArray { +export function __TS__SparseArrayNew(this: void, ...args: T[]): __TS__SparseArray { const sparseArray = [...args] as __TS__SparseArray; // select("#", ...) counts the number of args passed, including nils. // Note that we're depending on vararg optimization to occur here. diff --git a/src/lualib/SparseArrayPush.ts b/src/lualib/SparseArrayPush.ts index 30704620f..8a089a192 100644 --- a/src/lualib/SparseArrayPush.ts +++ b/src/lualib/SparseArrayPush.ts @@ -1,4 +1,6 @@ -function __TS__SparseArrayPush(this: void, sparseArray: __TS__SparseArray, ...args: T[]): void { +import { __TS__SparseArray } from "./SparseArray"; + +export function __TS__SparseArrayPush(this: void, sparseArray: __TS__SparseArray, ...args: T[]): void { const argsLen = select("#", ...args); const listLen = sparseArray.sparseLength; for (const i of $range(1, argsLen)) { diff --git a/src/lualib/SparseArraySpread.ts b/src/lualib/SparseArraySpread.ts index e365f19bc..99871cb89 100644 --- a/src/lualib/SparseArraySpread.ts +++ b/src/lualib/SparseArraySpread.ts @@ -1,4 +1,6 @@ -function __TS__SparseArraySpread(this: void, sparseArray: __TS__SparseArray): LuaMultiReturn { +import { __TS__SparseArray } from "./SparseArray"; + +export function __TS__SparseArraySpread(this: void, sparseArray: __TS__SparseArray): LuaMultiReturn { const _unpack = unpack ?? table.unpack; return _unpack(sparseArray, 1, sparseArray.sparseLength); } diff --git a/src/lualib/Spread.ts b/src/lualib/Spread.ts index 6bc3bf628..46ec8d5ae 100644 --- a/src/lualib/Spread.ts +++ b/src/lualib/Spread.ts @@ -1,4 +1,4 @@ -function __TS__Spread(this: void, iterable: string | Iterable): LuaMultiReturn { +export function __TS__Spread(this: void, iterable: string | Iterable): LuaMultiReturn { const arr = []; if (typeof iterable === "string") { // eslint-disable-next-line @typescript-eslint/prefer-for-of diff --git a/src/lualib/StringAccess.ts b/src/lualib/StringAccess.ts index 8695fd4e7..c273506c2 100644 --- a/src/lualib/StringAccess.ts +++ b/src/lualib/StringAccess.ts @@ -1,4 +1,4 @@ -function __TS__StringAccess(this: string, index: number) { +export function __TS__StringAccess(this: string, index: number) { if (index >= 0 && index < this.length) { return string.sub(this, index + 1, index + 1); } diff --git a/src/lualib/StringCharAt.ts b/src/lualib/StringCharAt.ts index 3c0e5161f..d76e94654 100644 --- a/src/lualib/StringCharAt.ts +++ b/src/lualib/StringCharAt.ts @@ -1,4 +1,4 @@ -function __TS__StringCharAt(this: string, pos: number): string { +export function __TS__StringCharAt(this: string, pos: number): string { if (pos !== pos) pos = 0; if (pos < 0) return ""; return string.sub(this, pos + 1, pos + 1); diff --git a/src/lualib/StringCharCodeAt.ts b/src/lualib/StringCharCodeAt.ts index 0044cc6cf..a3c722b9d 100644 --- a/src/lualib/StringCharCodeAt.ts +++ b/src/lualib/StringCharCodeAt.ts @@ -1,4 +1,4 @@ -function __TS__StringCharCodeAt(this: string, index: number): number { +export function __TS__StringCharCodeAt(this: string, index: number): number { if (index !== index) index = 0; if (index < 0) return NaN; return string.byte(this, index + 1) ?? NaN; diff --git a/src/lualib/StringConcat.ts b/src/lualib/StringConcat.ts index 657ae5c24..473040f31 100644 --- a/src/lualib/StringConcat.ts +++ b/src/lualib/StringConcat.ts @@ -1,4 +1,4 @@ -function __TS__StringConcat(this: void, str1: string, ...args: string[]): string { +export function __TS__StringConcat(this: void, str1: string, ...args: string[]): string { let out = str1; for (const arg of args) { out += arg; diff --git a/src/lualib/StringEndsWith.ts b/src/lualib/StringEndsWith.ts index b519a77eb..ea7d9020e 100644 --- a/src/lualib/StringEndsWith.ts +++ b/src/lualib/StringEndsWith.ts @@ -1,4 +1,4 @@ -function __TS__StringEndsWith(this: string, searchString: string, endPosition?: number): boolean { +export function __TS__StringEndsWith(this: string, searchString: string, endPosition?: number): boolean { if (endPosition === undefined || endPosition > this.length) { endPosition = this.length; } diff --git a/src/lualib/StringIncludes.ts b/src/lualib/StringIncludes.ts index 7508a2a42..3d896c8fa 100644 --- a/src/lualib/StringIncludes.ts +++ b/src/lualib/StringIncludes.ts @@ -1,4 +1,4 @@ -function __TS__StringIncludes(this: string, searchString: string, position?: number): boolean { +export function __TS__StringIncludes(this: string, searchString: string, position?: number): boolean { // http://lua-users.org/wiki/StringLibraryTutorial if (!position) { position = 1; diff --git a/src/lualib/StringPadEnd.ts b/src/lualib/StringPadEnd.ts index 24c8d48da..1f1c30e2e 100644 --- a/src/lualib/StringPadEnd.ts +++ b/src/lualib/StringPadEnd.ts @@ -1,4 +1,4 @@ -function __TS__StringPadEnd(this: string, maxLength: number, fillString = " "): string { +export function __TS__StringPadEnd(this: string, maxLength: number, fillString = " "): string { if (maxLength !== maxLength) maxLength = 0; if (maxLength === -Infinity || maxLength === Infinity) { throw "Invalid string length"; diff --git a/src/lualib/StringPadStart.ts b/src/lualib/StringPadStart.ts index 0e73f06fc..a6cb8e601 100644 --- a/src/lualib/StringPadStart.ts +++ b/src/lualib/StringPadStart.ts @@ -1,4 +1,4 @@ -function __TS__StringPadStart(this: string, maxLength: number, fillString = " "): string { +export function __TS__StringPadStart(this: string, maxLength: number, fillString = " "): string { if (maxLength !== maxLength) maxLength = 0; if (maxLength === -Infinity || maxLength === Infinity) { throw "Invalid string length"; diff --git a/src/lualib/StringReplace.ts b/src/lualib/StringReplace.ts index ab96d5329..f7de1da7a 100644 --- a/src/lualib/StringReplace.ts +++ b/src/lualib/StringReplace.ts @@ -1,4 +1,4 @@ -function __TS__StringReplace( +export function __TS__StringReplace( this: void, source: string, searchValue: string, diff --git a/src/lualib/StringReplaceAll.ts b/src/lualib/StringReplaceAll.ts index e288a1497..6911f92d7 100644 --- a/src/lualib/StringReplaceAll.ts +++ b/src/lualib/StringReplaceAll.ts @@ -1,4 +1,4 @@ -function __TS__StringReplaceAll( +export function __TS__StringReplaceAll( this: void, source: string, searchValue: string, diff --git a/src/lualib/StringSlice.ts b/src/lualib/StringSlice.ts index 42355d9e1..307d86894 100644 --- a/src/lualib/StringSlice.ts +++ b/src/lualib/StringSlice.ts @@ -1,4 +1,4 @@ -function __TS__StringSlice(this: string, start?: number, end?: number): string { +export function __TS__StringSlice(this: string, start?: number, end?: number): string { if (start === undefined || start !== start) start = 0; if (end !== end) end = 0; diff --git a/src/lualib/StringSplit.ts b/src/lualib/StringSplit.ts index 734dac38c..cf2b6ae9d 100644 --- a/src/lualib/StringSplit.ts +++ b/src/lualib/StringSplit.ts @@ -1,4 +1,4 @@ -function __TS__StringSplit(this: void, source: string, separator?: string, limit?: number): string[] { +export function __TS__StringSplit(this: void, source: string, separator?: string, limit?: number): string[] { if (limit === undefined) { limit = 4294967295; } diff --git a/src/lualib/StringStartsWith.ts b/src/lualib/StringStartsWith.ts index 8de8fa8ec..82f542143 100644 --- a/src/lualib/StringStartsWith.ts +++ b/src/lualib/StringStartsWith.ts @@ -1,4 +1,4 @@ -function __TS__StringStartsWith(this: string, searchString: string, position?: number): boolean { +export function __TS__StringStartsWith(this: string, searchString: string, position?: number): boolean { if (position === undefined || position < 0) { position = 0; } diff --git a/src/lualib/StringSubstr.ts b/src/lualib/StringSubstr.ts index e14c3c4a5..1940daf4d 100644 --- a/src/lualib/StringSubstr.ts +++ b/src/lualib/StringSubstr.ts @@ -1,4 +1,4 @@ -function __TS__StringSubstr(this: string, from: number, length?: number): string { +export function __TS__StringSubstr(this: string, from: number, length?: number): string { if (from !== from) from = 0; if (length !== undefined) { diff --git a/src/lualib/StringSubstring.ts b/src/lualib/StringSubstring.ts index df8210555..f3480081a 100644 --- a/src/lualib/StringSubstring.ts +++ b/src/lualib/StringSubstring.ts @@ -1,4 +1,4 @@ -function __TS__StringSubstring(this: string, start: number, end?: number): string { +export function __TS__StringSubstring(this: string, start: number, end?: number): string { if (end !== end) end = 0; if (end !== undefined && start > end) { diff --git a/src/lualib/StringTrim.ts b/src/lualib/StringTrim.ts index 8a231118a..f1d71d712 100644 --- a/src/lualib/StringTrim.ts +++ b/src/lualib/StringTrim.ts @@ -1,4 +1,4 @@ -function __TS__StringTrim(this: string): string { +export function __TS__StringTrim(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "^[%s\xA0\uFEFF]*(.-)[%s\xA0\uFEFF]*$", "%1"); return result; diff --git a/src/lualib/StringTrimEnd.ts b/src/lualib/StringTrimEnd.ts index c45238235..711c91538 100644 --- a/src/lualib/StringTrimEnd.ts +++ b/src/lualib/StringTrimEnd.ts @@ -1,4 +1,4 @@ -function __TS__StringTrimEnd(this: string): string { +export function __TS__StringTrimEnd(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "[%s\xA0\uFEFF]*$", ""); return result; diff --git a/src/lualib/StringTrimStart.ts b/src/lualib/StringTrimStart.ts index 47e6e8a66..258ea4f8b 100644 --- a/src/lualib/StringTrimStart.ts +++ b/src/lualib/StringTrimStart.ts @@ -1,4 +1,4 @@ -function __TS__StringTrimStart(this: string): string { +export function __TS__StringTrimStart(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "^[%s\xA0\uFEFF]*", ""); return result; diff --git a/src/lualib/Symbol.ts b/src/lualib/Symbol.ts index 67a1fd71b..d06eec37e 100644 --- a/src/lualib/Symbol.ts +++ b/src/lualib/Symbol.ts @@ -1,14 +1,14 @@ -const ____symbolMetatable = { +const symbolMetatable = { __tostring(this: symbol): string { return `Symbol(${this.description || ""})`; }, }; -function __TS__Symbol(this: void, description?: string | number): symbol { - return setmetatable({ description }, ____symbolMetatable) as unknown as symbol; +export function __TS__Symbol(this: void, description?: string | number): symbol { + return setmetatable({ description }, symbolMetatable) as unknown as symbol; } -Symbol = { +export const Symbol = { iterator: __TS__Symbol("Symbol.iterator"), hasInstance: __TS__Symbol("Symbol.hasInstance"), diff --git a/src/lualib/SymbolRegistry.ts b/src/lualib/SymbolRegistry.ts index 460edcc86..d8e68e3dd 100644 --- a/src/lualib/SymbolRegistry.ts +++ b/src/lualib/SymbolRegistry.ts @@ -1,15 +1,16 @@ -const ____symbolRegistry: Record = {}; +import { __TS__Symbol } from "./Symbol"; -function __TS__SymbolRegistryFor(this: void, key: string): symbol { - if (!____symbolRegistry[key]) { - ____symbolRegistry[key] = __TS__Symbol(key); +const symbolRegistry: Record = {}; +export function __TS__SymbolRegistryFor(this: void, key: string): symbol { + if (!symbolRegistry[key]) { + symbolRegistry[key] = __TS__Symbol(key); } - return ____symbolRegistry[key]; + return symbolRegistry[key]; } -function __TS__SymbolRegistryKeyFor(this: void, sym: symbol): string { - for (const key in ____symbolRegistry) { - if (____symbolRegistry[key] === sym) return key; +export function __TS__SymbolRegistryKeyFor(this: void, sym: symbol): string { + for (const key in symbolRegistry) { + if (symbolRegistry[key] === sym) return key; } } diff --git a/src/lualib/TypeOf.ts b/src/lualib/TypeOf.ts index 158062cd4..5afbbde04 100644 --- a/src/lualib/TypeOf.ts +++ b/src/lualib/TypeOf.ts @@ -1,4 +1,4 @@ -function __TS__TypeOf(this: void, value: unknown): string { +export function __TS__TypeOf(this: void, value: unknown): string { const luaType = type(value); if (luaType === "table") { return "object"; diff --git a/src/lualib/Unpack.ts b/src/lualib/Unpack.ts index 99aedf253..f138f2d7d 100644 --- a/src/lualib/Unpack.ts +++ b/src/lualib/Unpack.ts @@ -1 +1 @@ -const __TS__Unpack = table.unpack || unpack; +export const __TS__Unpack = table.unpack || unpack; diff --git a/src/lualib/WeakMap.ts b/src/lualib/WeakMap.ts index 7b7df1e99..e5f333293 100644 --- a/src/lualib/WeakMap.ts +++ b/src/lualib/WeakMap.ts @@ -1,4 +1,4 @@ -WeakMap = class WeakMap { +export class WeakMap { public static [Symbol.species] = WeakMap; public [Symbol.toStringTag] = "WeakMap"; @@ -46,4 +46,4 @@ WeakMap = class WeakMap { this.items.set(key, value); return this; } -}; +} diff --git a/src/lualib/WeakSet.ts b/src/lualib/WeakSet.ts index 670b3c695..0873861ab 100644 --- a/src/lualib/WeakSet.ts +++ b/src/lualib/WeakSet.ts @@ -1,4 +1,4 @@ -WeakSet = class WeakSet { +export class WeakSet { public static [Symbol.species] = WeakSet; public [Symbol.toStringTag] = "WeakSet"; @@ -40,4 +40,4 @@ WeakSet = class WeakSet { public has(value: T): boolean { return this.items.get(value) === true; } -}; +} diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index 5897dbad9..ba21ec3a8 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -11,6 +11,7 @@ }, "tstl": { "luaLibImport": "none", - "noHeader": true + "noHeader": true, + "luaLibProject": true } } diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index b56486425..dee199f7b 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -33,6 +33,15 @@ export function transformBuiltinPropertyAccessExpression( ): lua.Expression | undefined { const ownerType = context.checker.getTypeAtLocation(node.expression); + if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) { + switch (node.expression.text) { + case "Math": + return transformMathProperty(context, node); + case "Symbol": + importLuaLibFeature(context, LuaLibFeature.Symbol); + } + } + if (isStringType(context, ownerType)) { return transformStringProperty(context, node); } @@ -44,15 +53,6 @@ export function transformBuiltinPropertyAccessExpression( if (isFunctionType(ownerType)) { return transformFunctionProperty(context, node); } - - if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) { - switch (node.expression.text) { - case "Math": - return transformMathProperty(context, node); - case "Symbol": - importLuaLibFeature(context, LuaLibFeature.Symbol); - } - } } export function transformBuiltinCallExpression( diff --git a/src/transformation/builtins/promise.ts b/src/transformation/builtins/promise.ts index cd30aa718..93c29598d 100644 --- a/src/transformation/builtins/promise.ts +++ b/src/transformation/builtins/promise.ts @@ -11,10 +11,6 @@ export function isPromiseClass(context: TransformationContext, node: ts.Identifi return isStandardLibraryType(context, type, undefined) && node.text === "Promise"; } -export function createPromiseIdentifier(original: ts.Node) { - return lua.createIdentifier("__TS__Promise", original); -} - export function transformPromiseConstructorCall( context: TransformationContext, node: PropertyCallExpression diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index 9508479ce..3517bcfcc 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -146,11 +146,17 @@ export function createLocalOrExportedOrGlobalDeclaration( if (!rhs) { return []; } else { - assignment = lua.createAssignmentStatement( - identifiers.map(identifier => createExportedIdentifier(context, identifier, exportScope)), - rhs, - tsOriginal - ); + if (context.options.luaLibProject) { + // assignment to a local variable defined elsewhere + // see sourceFile.ts + assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); + } else { + assignment = lua.createAssignmentStatement( + identifiers.map(identifier => createExportedIdentifier(context, identifier, exportScope)), + rhs, + tsOriginal + ); + } } } else { const scope = peekScope(context); diff --git a/src/transformation/visitors/class/setup.ts b/src/transformation/visitors/class/setup.ts index 151e68a77..36e9e7411 100644 --- a/src/transformation/visitors/class/setup.ts +++ b/src/transformation/visitors/class/setup.ts @@ -40,7 +40,7 @@ export function createClassSetup( result.push(lua.createVariableDeclarationStatement(localClassName, defaultExportLeftHandSide)); } else { const exportScope = getIdentifierExportScope(context, className); - if (exportScope) { + if (exportScope && !context.options.luaLibProject) { // local localClassName = ____exports.className result.push( lua.createVariableDeclarationStatement( diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index 46c3a8904..685923520 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { transformBuiltinIdentifierExpression } from "../builtins"; -import { createPromiseIdentifier, isPromiseClass } from "../builtins/promise"; +import { isPromiseClass } from "../builtins/promise"; import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context"; import { AnnotationKind, isForRangeType } from "../utils/annotations"; import { @@ -57,7 +57,11 @@ export function transformIdentifier(context: TransformationContext, identifier: if (isPromiseClass(context, identifier)) { importLuaLibFeature(context, LuaLibFeature.Promise); - return createPromiseIdentifier(identifier); + return lua.createIdentifier("__TS__Promise", identifier); + } + if (isPromiseClass(context, identifier)) { + importLuaLibFeature(context, LuaLibFeature.Promise); + return lua.createIdentifier("__TS__Promise", identifier); } const text = hasUnsafeIdentifierName(context, identifier) ? createSafeName(identifier.text) : identifier.text; @@ -68,7 +72,7 @@ export function transformIdentifier(context: TransformationContext, identifier: export const transformIdentifierExpression: FunctionVisitor = (node, context) => { const symbol = context.checker.getSymbolAtLocation(node); - if (symbol) { + if (symbol && !context.options.luaLibProject) { const exportScope = getSymbolExportScope(context, symbol); if (exportScope) { const name = symbol.name; diff --git a/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index d8daba8c9..8f755dd8d 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -1,6 +1,7 @@ import * as path from "path"; import * as ts from "typescript"; import * as lua from "../../../LuaAST"; +import { assert } from "../../../utils"; import { FunctionVisitor, TransformationContext } from "../../context"; import { AnnotationKind, getSymbolAnnotations } from "../../utils/annotations"; import { createDefaultExportStringLiteral } from "../../utils/export"; @@ -9,6 +10,8 @@ import { createSafeName } from "../../utils/safe-names"; import { peekScope } from "../../utils/scope"; import { transformIdentifier } from "../identifier"; import { transformPropertyName } from "../literal"; +import { LuaLibFeature } from "../../../LuaLib"; +import { importLuaLibFeature } from "../../utils/lualib"; function isNoResolutionPath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { const moduleOwnerSymbol = context.checker.getSymbolAtLocation(moduleSpecifier); @@ -58,6 +61,19 @@ function transformImportSpecifier( } export const transformImportDeclaration: FunctionVisitor = (statement, context) => { + if (context.options.luaLibProject) { + // in lualib project, imports declare dependencies + // Assume that relevant imports are in the form `import "./module"` + + const moduleSpecifier = statement.moduleSpecifier; + assert(ts.isStringLiteral(moduleSpecifier)); + const moduleName = moduleSpecifier.text.match(/\.\/(.*)$/)?.[1]; + if (moduleName && moduleName in LuaLibFeature) { + importLuaLibFeature(context, moduleName as LuaLibFeature); + } + return undefined; + } + const scope = peekScope(context); if (!scope.importStatements) { @@ -72,12 +88,8 @@ export const transformImportDeclaration: FunctionVisitor = if (statement.importClause === undefined) { result.push(lua.createExpressionStatement(requireCall)); - if (scope.importStatements) { - scope.importStatements.push(...result); - return undefined; - } else { - return result; - } + scope.importStatements.push(...result); + return undefined; } const importPath = ts.isStringLiteral(statement.moduleSpecifier) @@ -144,12 +156,8 @@ export const transformImportDeclaration: FunctionVisitor = result.unshift(lua.createVariableDeclarationStatement(importUniqueName, requireCall, statement)); } - if (scope.importStatements) { - scope.importStatements.push(...result); - return undefined; - } else { - return result; - } + scope.importStatements.push(...result); + return undefined; }; export const transformExternalModuleReference: FunctionVisitor = (node, context) => diff --git a/src/transformation/visitors/sourceFile.ts b/src/transformation/visitors/sourceFile.ts index 903ec0a11..fdfdda425 100644 --- a/src/transformation/visitors/sourceFile.ts +++ b/src/transformation/visitors/sourceFile.ts @@ -1,5 +1,6 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; +import { SymbolId } from "../../LuaAST"; import { assert } from "../../utils"; import { FunctionVisitor } from "../context"; import { createExportsIdentifier } from "../utils/lua-ast"; @@ -7,9 +8,12 @@ import { getUsedLuaLibFeatures } from "../utils/lualib"; import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope"; import { hasExportEquals } from "../utils/typescript"; +import { getSymbolIdOfSymbol, getSymbolInfo } from "../utils/symbols"; +import { getExportedSymbolsFromScope } from "../utils/export"; export const transformSourceFileNode: FunctionVisitor = (node, context) => { let statements: lua.Statement[] = []; + let exports: string[] | undefined; if (node.flags & ts.NodeFlags.JsonFile) { const [statement] = node.statements; if (statement) { @@ -28,11 +32,39 @@ export const transformSourceFileNode: FunctionVisitor = (node, co } } else { pushScope(context, ScopeType.File); - statements = performHoisting(context, context.transformStatements(node.statements)); popScope(context); - if (context.isModule) { + if (context.options.luaLibProject) { + // Exports are currently assignment statements + const exportedSymbolIds = getExportedSymbolsFromScope(context, context.sourceFile) + .map(symbol => getSymbolIdOfSymbol(context, symbol)) + .filter(id => id !== undefined) as SymbolId[]; + const allStatementsAreExports = statements.every( + s => + lua.isAssignmentStatement(s) && + s.left.every(l => lua.isIdentifier(l) && exportedSymbolIds.find(id => id === l.symbolId)) + ); + if (allStatementsAreExports) { + // can convert all to variable declarations + statements = statements.map(statement => { + assert(lua.isAssignmentStatement(statement)); + return lua.createVariableDeclarationStatement(statement.left as lua.Identifier[], statement.right); + }); + } else { + // not all statements are exports + // declare exports in variable declaration at top level, wrap statements in a do block + // this is so that non-exported statements do not leak into scope + const exportedIdentifiers = exportedSymbolIds.map(id => + lua.createIdentifier(getSymbolInfo(context, id)!.symbol.getName()) + ); + statements = [ + lua.createVariableDeclarationStatement(exportedIdentifiers), + lua.createDoStatement(statements), + ]; + } + exports = exportedSymbolIds.map(id => getSymbolInfo(context, id)!.symbol.getName()); + } else if (context.isModule) { // If export equals was not used. Create the exports table. // local ____exports = {} if (!hasExportEquals(node)) { @@ -47,5 +79,5 @@ export const transformSourceFileNode: FunctionVisitor = (node, co } const trivia = node.getFullText().match(/^#!.*\r?\n/)?.[0] ?? ""; - return lua.createFile(statements, getUsedLuaLibFeatures(context), trivia, node); + return lua.createFile(statements, getUsedLuaLibFeatures(context), trivia, node, exports); }; diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 45fe6d409..355dc6558 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -104,6 +104,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const footers: string[] = []; if (options.sourceMapTraceback) { // Generates SourceMapTraceback for the entire file + // TODO: remove footers.push('require("lualib_bundle")\n'); footers.push(`${sourceMapTracebackBundlePlaceholder}\n`); } diff --git a/src/transpilation/lualib.ts b/src/transpilation/lualib.ts new file mode 100644 index 000000000..0edfe28aa --- /dev/null +++ b/src/transpilation/lualib.ts @@ -0,0 +1,67 @@ +import { ProcessedFile, EmitHost } from "./utils"; +import { LuaLibFeature, LuaLibModulesInfo, loadInlineLualibFeatures } from "../LuaLib"; +import * as path from "path"; +import { LuaPrinter } from "../LuaPrinter"; +import * as ts from "typescript"; +import * as lua from "../LuaAST"; + +export function createLuaLibModuleInfo(files: ProcessedFile[]): LuaLibModulesInfo { + const luaLibFiles: Map = new Map(); + + for (const file of files) { + if (!file.luaAst) continue; + const fileName = path.basename(file.fileName, ".ts"); + if (!(fileName in LuaLibFeature)) { + // TODO: Diagnostic? + console.error(`File does not correspond to a LuaLibFeature: ${fileName}`); + } else { + luaLibFiles.set(fileName as LuaLibFeature, file); + } + } + for (const luaLibFeature of Object.values(LuaLibFeature)) { + if (!luaLibFiles.has(luaLibFeature)) { + console.error(`Missing file for LuaLibFeature: ${luaLibFeature}`); + } + } + + const result: Partial = {}; + for (const [feature, file] of luaLibFiles) { + let dependencies: LuaLibFeature[] | undefined; + const dependenciesForFeature = file.luaAst!.luaLibFeatures; + dependenciesForFeature.delete(feature); // Don't include self + if (dependenciesForFeature.size > 0) { + dependencies = Array.from(dependenciesForFeature); + } + + const exports = file.luaAst!.exports!; + if (dependencies || exports) { + result[feature] = { + dependencies, + exports, + }; + } + } + + return result as LuaLibModulesInfo; +} + +export function createLuaLibBundle( + emitHost: EmitHost, + program: ts.Program, + luaLibModuleInfo: LuaLibModulesInfo +): string { + const allFeatures = Object.values(LuaLibFeature) as LuaLibFeature[]; + + let result = loadInlineLualibFeatures(allFeatures, emitHost); + + const exports = allFeatures.flatMap(feature => luaLibModuleInfo[feature].exports); + const statements: lua.TableFieldExpression[] = exports.map(exportName => + lua.createTableFieldExpression(lua.createIdentifier(exportName), lua.createStringLiteral(exportName)) + ); + const moduleReturn = lua.createReturnStatement([lua.createTableExpression(statements)]); + + const printer = new LuaPrinter(emitHost, program, "lualib_bundle.lua"); + result += `\n${printer.printReturnStatement(moduleReturn)}\n`; + + return result; +} diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index fc645147f..90991f8d8 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -7,6 +7,7 @@ import { isNonNull } from "../utils"; import { getPlugins, Plugin } from "./plugins"; import { getTransformers } from "./transformers"; import { EmitHost, ProcessedFile } from "./utils"; +import { createLuaLibModuleInfo, createLuaLibBundle } from "./lualib"; export interface TranspileOptions { program: ts.Program; @@ -123,6 +124,24 @@ export function getProgramTranspileResult( program.getSourceFiles().filter(isEmittableJsonFile).forEach(processSourceFile); } + if (options.luaLibProject) { + // add lualib dependencies json file + const dependencyInfo = createLuaLibModuleInfo(transpiledFiles); + transpiledFiles.push({ + code: JSON.stringify(dependencyInfo, null, 2), + fileName: "lualib_dependencies.json", + isRawFile: true, + }); + + // add lualib bundle file + const bundle = createLuaLibBundle(emitHost, program, dependencyInfo); + transpiledFiles.push({ + code: bundle, + fileName: "lualib_bundle.lua", + isRawFile: true, + }); + } + options.noEmit = oldNoEmit; if (options.noEmit || (options.noEmitOnError && diagnostics.length > 0)) { diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index e30410f73..79e4eaf29 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -23,6 +23,7 @@ export interface EmitResult { export class Transpiler { protected emitHost: EmitHost; + constructor({ emitHost = ts.sys }: TranspilerOptions = {}) { this.emitHost = emitHost; } @@ -99,7 +100,7 @@ export class Transpiler { } else { emitPlan = resolutionResult.resolvedFiles.map(file => ({ ...file, - outputPath: getEmitPath(file.fileName, program), + outputPath: getEmitPath(file, program), })); } @@ -107,9 +108,13 @@ export class Transpiler { } } -export function getEmitPath(file: string, program: ts.Program): string { - const relativeOutputPath = getEmitPathRelativeToOutDir(file, program); +export function getEmitPath(file: string | ProcessedFile, program: ts.Program): string { const outDir = getEmitOutDir(program); + if (typeof file !== "string" && file.isRawFile) { + return path.join(outDir, file.fileName); + } + const fileName = typeof file === "string" ? file : file.fileName; + const relativeOutputPath = getEmitPathRelativeToOutDir(fileName, program); return path.join(outDir, relativeOutputPath); } diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index 0bf8075d2..4ccd5cc40 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -19,6 +19,7 @@ interface BaseFile { code: string; sourceMap?: string; sourceFiles?: ts.SourceFile[]; + isRawFile?: boolean; } export interface ProcessedFile extends BaseFile { diff --git a/test/transpile/lualib.spec.ts b/test/transpile/lualib.spec.ts index 0af391c46..f05ffda2b 100644 --- a/test/transpile/lualib.spec.ts +++ b/test/transpile/lualib.spec.ts @@ -1,26 +1,10 @@ import * as ts from "typescript"; import { LuaLibFeature } from "../../src"; -import { loadLuaLibFeatures } from "../../src/LuaLib"; +import { readLuaLibFeature } from "../../src/LuaLib"; -test.each(Object.entries(LuaLibFeature))("Lualib feature has correct dependencies (%p)", (_, feature) => { - const lualibCode = loadLuaLibFeatures([feature], ts.sys); +test.each(Object.entries(LuaLibFeature))("Lualib does not use ____exports (%p)", (_, feature) => { + const lualibCode = readLuaLibFeature(feature, ts.sys); - // Find all used lualib features - const luaLibReferences = lualibCode.match(/__TS__[a-zA-Z_]+\(/g); - - // For every reference lualib function, check if its definition is also included - const missingReferences = []; - - if (luaLibReferences !== null) { - for (const reference of luaLibReferences) { - if ( - !lualibCode.includes(`function ${reference}`) && - !lualibCode.includes(`${reference.substring(0, reference.length - 1)} =`) - ) { - missingReferences.push(reference); - } - } - } - - expect(missingReferences).toHaveLength(0); + const exportsOccurrences = lualibCode.match(/____exports/g); + expect(exportsOccurrences).toBeNull(); }); diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index c363dbea1..244432ead 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -490,7 +490,7 @@ test("includes lualib_bundle when external lua requests it", () => { .addExtraFile( "lualibuser.lua", ` - require("lualib_bundle") + local __TS__ArrayPush = require("lualib_bundle").__TS__ArrayPush local result = {} __TS__ArrayPush(result, "foo") From f4f7a929327081e543feff22770a8395160d1ca3 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 14:33:40 -0800 Subject: [PATCH 02/12] Fix tests and update snapshots --- src/LuaPrinter.ts | 8 +++++--- src/transformation/builtins/error.ts | 18 ++++++++++++++++++ src/transformation/visitors/identifier.ts | 7 ++++--- src/transpilation/bundle.ts | 2 +- .../__snapshots__/transformation.spec.ts.snap | 9 ++++++--- .../__snapshots__/expressions.spec.ts.snap | 12 ++++++++---- .../optionalChaining.spec.ts.snap | 3 ++- test/unit/__snapshots__/switch.spec.ts.snap | 9 ++++++--- .../customConstructor.spec.ts.snap | 4 +++- .../__snapshots__/deprecated.spec.ts.snap | 15 +++++++++++---- .../classes/__snapshots__/classes.spec.ts.snap | 3 ++- .../__snapshots__/decorators.spec.ts.snap | 4 +++- .../__snapshots__/range.spec.ts.snap | 6 ++++-- test/unit/printer/sourcemaps.spec.ts | 2 +- 14 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 src/transformation/builtins/error.ts diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 0d4863ac3..d73c52148 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -173,7 +173,7 @@ export class LuaPrinter { public print(file: lua.File): PrintResult { // Add traceback lualib if sourcemap traceback option is enabled - if (this.options.sourceMapTraceback) { + if (this.options.sourceMapTraceback && !isBundleEnabled(this.options)) { file.luaLibFeatures.add(LuaLibFeature.SourceMapTraceBack); } @@ -232,6 +232,7 @@ export class LuaPrinter { if (!this.options.noHeader) { header += tstlHeader; } + let statements = file.statements; const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require; if ( @@ -244,7 +245,8 @@ export class LuaPrinter { this.emitHost, luaLibImport === LuaLibImportKind.Always ); - header += this.concatNodes(...this.printStatementArray(importStatements)).toString(); + + statements = importStatements.concat(statements); } else if (luaLibImport === LuaLibImportKind.Inline && file.luaLibFeatures.size > 0) { // Inline lualib features header += "-- Lua Library inline imports\n"; @@ -257,7 +259,7 @@ export class LuaPrinter { header += `${LuaPrinter.sourceMapTracebackPlaceholder}\n`; } - return this.concatNodes(header, ...this.printStatementArray(file.statements)); + return this.concatNodes(header, ...this.printStatementArray(statements)); } protected pushIndent(): void { diff --git a/src/transformation/builtins/error.ts b/src/transformation/builtins/error.ts new file mode 100644 index 000000000..84b9bb314 --- /dev/null +++ b/src/transformation/builtins/error.ts @@ -0,0 +1,18 @@ +import { TransformationContext } from "../context"; +import * as ts from "typescript"; +import { isStandardLibraryType } from "../utils/typescript"; + +const errorClasses = new Set([ + "Error", + "EvalError", + "RangeError", + "ReferenceError", + "SyntaxError", + "TypeError", + "URIError", +]); + +export function isErrorClass(context: TransformationContext, node: ts.Identifier) { + const type = context.checker.getTypeAtLocation(node); + return isStandardLibraryType(context, type, undefined) && errorClasses.has(node.text); +} diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index 685923520..b7e142c14 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -22,6 +22,7 @@ import { isRangeFunctionNode } from "./language-extensions/range"; import { isTableExtensionIdentifier } from "./language-extensions/table"; import { isVarargConstantNode } from "./language-extensions/vararg"; import { isOptionalContinuation } from "./optional-chaining"; +import { isErrorClass } from "../builtins/error"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { if (isOptionalContinuation(identifier)) { @@ -59,9 +60,9 @@ export function transformIdentifier(context: TransformationContext, identifier: importLuaLibFeature(context, LuaLibFeature.Promise); return lua.createIdentifier("__TS__Promise", identifier); } - if (isPromiseClass(context, identifier)) { - importLuaLibFeature(context, LuaLibFeature.Promise); - return lua.createIdentifier("__TS__Promise", identifier); + + if (isErrorClass(context, identifier)) { + importLuaLibFeature(context, LuaLibFeature.Error); } const text = hasUnsafeIdentifierName(context, identifier) ? createSafeName(identifier.text) : identifier.text; diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 355dc6558..3b9a1287a 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -105,7 +105,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t if (options.sourceMapTraceback) { // Generates SourceMapTraceback for the entire file // TODO: remove - footers.push('require("lualib_bundle")\n'); + footers.push('local __TS__SourceMapTraceBack = require("lualib_bundle").__TS__SourceMapTraceBack\n'); footers.push(`${sourceMapTracebackBundlePlaceholder}\n`); } diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 9671f656c..32f48994b 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -58,7 +58,8 @@ return ____exports" `; exports[`Transformation (methodRestArguments) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class MyClass = __TS__Class() MyClass.name = \\"MyClass\\" function MyClass.prototype.____constructor(self) @@ -74,7 +75,8 @@ return ____exports" `; exports[`Transformation (modulesClassExport) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class local ____exports = {} ____exports.TestClass = __TS__Class() local TestClass = ____exports.TestClass @@ -85,7 +87,8 @@ return ____exports" `; exports[`Transformation (modulesClassWithMemberExport) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class local ____exports = {} ____exports.TestClass = __TS__Class() local TestClass = ____exports.TestClass diff --git a/test/unit/__snapshots__/expressions.spec.ts.snap b/test/unit/__snapshots__/expressions.spec.ts.snap index 91bc2306b..5f58cde14 100644 --- a/test/unit/__snapshots__/expressions.spec.ts.snap +++ b/test/unit/__snapshots__/expressions.spec.ts.snap @@ -503,7 +503,8 @@ return ____exports" `; exports[`Unary expressions basic ("delete tbl.test") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) __TS__Delete(tbl, \\"test\\") @@ -512,7 +513,8 @@ return ____exports" `; exports[`Unary expressions basic ("delete tbl['test']") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) __TS__Delete(tbl, \\"test\\") @@ -537,7 +539,8 @@ return ____exports" `; exports[`Unary expressions basic ("let a = delete tbl.test") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) local a = __TS__Delete(tbl, \\"test\\") @@ -546,7 +549,8 @@ return ____exports" `; exports[`Unary expressions basic ("let a = delete tbl['test']") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) local a = __TS__Delete(tbl, \\"test\\") diff --git a/test/unit/__snapshots__/optionalChaining.spec.ts.snap b/test/unit/__snapshots__/optionalChaining.spec.ts.snap index 46ac47aa8..3d67bf838 100644 --- a/test/unit/__snapshots__/optionalChaining.spec.ts.snap +++ b/test/unit/__snapshots__/optionalChaining.spec.ts.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Unsupported optional chains Builtin global method: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Number = ____lualib.__TS__Number local ____Number_result_0 = Number if ____Number_result_0 ~= nil then ____Number_result_0 = nil diff --git a/test/unit/__snapshots__/switch.spec.ts.snap b/test/unit/__snapshots__/switch.spec.ts.snap index 45ba87e16..a640878c4 100644 --- a/test/unit/__snapshots__/switch.spec.ts.snap +++ b/test/unit/__snapshots__/switch.spec.ts.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`switch empty fallthrough to default (0) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__ArrayPush = ____lualib.__TS__ArrayPush local ____exports = {} function ____exports.__main(self) local out = {} @@ -18,7 +19,8 @@ return ____exports" `; exports[`switch empty fallthrough to default (1) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__ArrayPush = ____lualib.__TS__ArrayPush local ____exports = {} function ____exports.__main(self) local out = {} @@ -111,7 +113,8 @@ return ____exports" `; exports[`switch produces optimal output 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__ArrayPush = ____lualib.__TS__ArrayPush local ____exports = {} function ____exports.__main(self) local x = 0 diff --git a/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap b/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap index c283485bf..ffd43b2b6 100644 --- a/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap +++ b/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap @@ -1,7 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`IncorrectUsage: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class +local __TS__New = ____lualib.__TS__New local ____exports = {} function ____exports.__main(self) local Point2D = __TS__Class() diff --git a/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap b/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap index a962a4a30..32c01be9e 100644 --- a/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap +++ b/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap @@ -11,7 +11,8 @@ return ____exports" exports[`LuaTable deprecation warning property access set: diagnostics 1`] = `"main.ts(12,16): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; exports[`LuaTable removed warning constructor: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__New = ____lualib.__TS__New ____table = __TS__New(Table)" `; @@ -38,14 +39,18 @@ return ____exports" exports[`LuaTable removed warning property access length: diagnostics 1`] = `"main.ts(12,16): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; exports[`extension removed: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class +local __TS__ClassExtends = ____lualib.__TS__ClassExtends B = __TS__Class() B.name = \\"B\\" __TS__ClassExtends(B, A)" `; exports[`extension removed: code 2`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class +local __TS__ClassExtends = ____lualib.__TS__ClassExtends B = __TS__Class() B.name = \\"B\\" __TS__ClassExtends(B, A)" @@ -90,7 +95,9 @@ end" exports[`phantom removed: diagnostics 1`] = `"main.ts(3,9): error TSTL: '@phantom' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#phantom for more information."`; exports[`pureAbstract removed: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class +local __TS__ClassExtends = ____lualib.__TS__ClassExtends ClassB = __TS__Class() ClassB.name = \\"ClassB\\" __TS__ClassExtends(ClassB, ClassA)" diff --git a/test/unit/classes/__snapshots__/classes.spec.ts.snap b/test/unit/classes/__snapshots__/classes.spec.ts.snap index 6336a53a3..7822867f5 100644 --- a/test/unit/classes/__snapshots__/classes.spec.ts.snap +++ b/test/unit/classes/__snapshots__/classes.spec.ts.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`missing declaration name: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class ____class_0 = __TS__Class() ____class_0.name = \\"\\" function ____class_0.prototype.____constructor(self) diff --git a/test/unit/classes/__snapshots__/decorators.spec.ts.snap b/test/unit/classes/__snapshots__/decorators.spec.ts.snap index cbc983bd5..cef76f35e 100644 --- a/test/unit/classes/__snapshots__/decorators.spec.ts.snap +++ b/test/unit/classes/__snapshots__/decorators.spec.ts.snap @@ -1,7 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Throws error if decorator function has void context: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Class = ____lualib.__TS__Class +local __TS__Decorate = ____lualib.__TS__Decorate local ____exports = {} function ____exports.__main(self) local function decorator(constructor) diff --git a/test/unit/language-extensions/__snapshots__/range.spec.ts.snap b/test/unit/language-extensions/__snapshots__/range.spec.ts.snap index c1f369cb6..64273b8fe 100644 --- a/test/unit/language-extensions/__snapshots__/range.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/range.spec.ts.snap @@ -33,7 +33,8 @@ return ____exports" exports[`$range invalid use ("const x = $range(1, 10);"): diagnostics 1`] = `"main.ts(2,19): error TSTL: $range can only be used in a for...of loop."`; exports[`$range invalid use ("const y = [...$range(1, 10)];"): code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Spread = ____lualib.__TS__Spread local ____exports = {} function ____exports.__main(self) local y = {__TS__Spread(____(nil, 1, 10))} @@ -66,7 +67,8 @@ return ____exports" exports[`$range invalid use ("for (const i of $range(1, 10, 2) as number[]) {}"): diagnostics 1`] = `"main.ts(2,25): error TSTL: $range can only be used in a for...of loop."`; exports[`$range invalid use ("for (const i of ($range(1, 10, 2))) {}"): code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require(\\"lualib_bundle\\") +local __TS__Iterator = ____lualib.__TS__Iterator local ____exports = {} function ____exports.__main(self) for ____, i in __TS__Iterator(____(nil, 1, 10, 2)) do diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index fdc21a13a..b625f1f5f 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -88,7 +88,7 @@ test.each([ assertPatterns: [ { luaPattern: "Bar = __TS__Class()", typeScriptPattern: "class Bar" }, { luaPattern: "Bar.name =", typeScriptPattern: "class Bar" }, - { luaPattern: "__TS__ClassExtends", typeScriptPattern: "extends" }, + { luaPattern: "__TS__ClassExtends(", typeScriptPattern: "extends" }, // find use of function, not import { luaPattern: "Foo", typeScriptPattern: "Foo" }, { luaPattern: "function Bar.prototype.____constructor", typeScriptPattern: "constructor" }, ], From 6b9e7a87ac1fe45922c041b14f49c518757d946a Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 15:06:44 -0800 Subject: [PATCH 03/12] Fix lualib_bundle.lua not being updated immediately when lualib source changes --- src/LuaLib.ts | 12 +++++--- src/lualib/Promise.ts | 26 ++++++++-------- src/lualib/PromiseAll.ts | 6 ++-- src/lualib/PromiseAllSettled.ts | 6 ++-- src/lualib/PromiseAny.ts | 6 ++-- src/lualib/PromiseRace.ts | 6 ++-- src/transpilation/lualib.ts | 54 ++++++++++++++++++++------------- src/transpilation/transpile.ts | 17 ++--------- 8 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/LuaLib.ts b/src/LuaLib.ts index e8fd80226..4cacb5bbc 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -129,21 +129,25 @@ export function readLuaLibFeature(feature: LuaLibFeature, emitHost: EmitHost): s return luaLibFeature; } -export function loadInlineLualibFeatures(features: Iterable, emitHost: EmitHost): string { +export function loadInlineLualibFeatures( + features: Iterable, + emitHost: EmitHost, + luaLibModulesInfo: LuaLibModulesInfo = getLuaLibModuleInfo(emitHost), + readFeature: (feature: LuaLibFeature) => string = feature => readLuaLibFeature(feature, emitHost) +): string { let result = ""; const loadedFeatures = new Set(); - const luaLibDependencyMap = getLuaLibModuleInfo(emitHost); function load(feature: LuaLibFeature): void { if (loadedFeatures.has(feature)) return; loadedFeatures.add(feature); - const dependencies = luaLibDependencyMap[feature]?.dependencies; + const dependencies = luaLibModulesInfo[feature]?.dependencies; if (dependencies) { dependencies.forEach(load); } - const luaLibFeature = readLuaLibFeature(feature, emitHost); + const luaLibFeature = readFeature(feature); result += luaLibFeature + "\n"; } diff --git a/src/lualib/Promise.ts b/src/lualib/Promise.ts index bc44443ef..99f144c0e 100644 --- a/src/lualib/Promise.ts +++ b/src/lualib/Promise.ts @@ -3,7 +3,7 @@ // Promises implemented based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise // and https://promisesaplus.com/ -export enum __TS__PromiseState { +export const enum PromiseState { Pending, Fulfilled, Rejected, @@ -28,7 +28,7 @@ function isPromiseLike(thing: unknown): thing is PromiseLike { } export class __TS__Promise implements Promise { - public state = __TS__PromiseState.Pending; + public state = PromiseState.Pending; public value?: T; public rejectionReason?: any; @@ -42,7 +42,7 @@ export class __TS__Promise implements Promise { public static resolve(this: void, data: TData): Promise { // Create and return a promise instance that is already resolved const promise = new __TS__Promise(() => {}); - promise.state = __TS__PromiseState.Fulfilled; + promise.state = PromiseState.Fulfilled; promise.value = data; return promise; } @@ -51,7 +51,7 @@ export class __TS__Promise implements Promise { public static reject(this: void, reason: any): Promise { // Create and return a promise instance that is already rejected const promise = new __TS__Promise(() => {}); - promise.state = __TS__PromiseState.Rejected; + promise.state = PromiseState.Rejected; promise.rejectionReason = reason; return promise; } @@ -72,8 +72,8 @@ export class __TS__Promise implements Promise { ): Promise { const { promise, resolve, reject } = promiseDeferred(); - const isFulfilled = this.state === __TS__PromiseState.Fulfilled; - const isRejected = this.state === __TS__PromiseState.Rejected; + const isFulfilled = this.state === PromiseState.Fulfilled; + const isRejected = this.state === PromiseState.Rejected; if (onFulfilled) { const internalCallback = this.createPromiseResolvingCallback(onFulfilled, resolve, reject); @@ -121,7 +121,7 @@ export class __TS__Promise implements Promise { if (onFinally) { this.finallyCallbacks.push(onFinally); - if (this.state !== __TS__PromiseState.Pending) { + if (this.state !== PromiseState.Pending) { // If promise already resolved or rejected, immediately fire finally callback onFinally(); } @@ -139,8 +139,8 @@ export class __TS__Promise implements Promise { } // Resolve this promise, if it is still pending. This function is passed to the constructor function. - if (this.state === __TS__PromiseState.Pending) { - this.state = __TS__PromiseState.Fulfilled; + if (this.state === PromiseState.Pending) { + this.state = PromiseState.Fulfilled; this.value = data; for (const callback of this.fulfilledCallbacks) { @@ -154,8 +154,8 @@ export class __TS__Promise implements Promise { private reject(reason: any): void { // Reject this promise, if it is still pending. This function is passed to the constructor function. - if (this.state === __TS__PromiseState.Pending) { - this.state = __TS__PromiseState.Rejected; + if (this.state === PromiseState.Pending) { + this.state = PromiseState.Rejected; this.rejectionReason = reason; for (const callback of this.rejectedCallbacks) { @@ -189,11 +189,11 @@ export class __TS__Promise implements Promise { ) { if (isPromiseLike(data)) { const nextpromise = data as __TS__Promise; - if (nextpromise.state === __TS__PromiseState.Fulfilled) { + if (nextpromise.state === PromiseState.Fulfilled) { // If a handler function returns an already fulfilled promise, // the promise returned by then gets fulfilled with that promise's value resolve(nextpromise.value); - } else if (nextpromise.state === __TS__PromiseState.Rejected) { + } else if (nextpromise.state === PromiseState.Rejected) { // If a handler function returns an already rejected promise, // the promise returned by then gets fulfilled with that promise's value reject(nextpromise.rejectionReason); diff --git a/src/lualib/PromiseAll.ts b/src/lualib/PromiseAll.ts index 5283ba49f..c9263b645 100644 --- a/src/lualib/PromiseAll.ts +++ b/src/lualib/PromiseAll.ts @@ -1,5 +1,5 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all -import { __TS__Promise, __TS__PromiseState } from "./Promise"; +import { __TS__Promise, PromiseState } from "./Promise"; // eslint-disable-next-line @typescript-eslint/promise-function-async export function __TS__PromiseAll(this: void, iterable: Iterable>): Promise { @@ -11,10 +11,10 @@ export function __TS__PromiseAll(this: void, iterable: Iterable( @@ -14,10 +14,10 @@ export function __TS__PromiseAllSettled( let i = 0; for (const item of iterable) { if (item instanceof __TS__Promise) { - if (item.state === __TS__PromiseState.Fulfilled) { + if (item.state === PromiseState.Fulfilled) { // If value is a resolved promise, add a fulfilled PromiseSettledResult results[i] = { status: "fulfilled", value: item.value }; - } else if (item.state === __TS__PromiseState.Rejected) { + } else if (item.state === PromiseState.Rejected) { // If value is a rejected promise, add a rejected PromiseSettledResult results[i] = { status: "rejected", reason: item.rejectionReason }; } else { diff --git a/src/lualib/PromiseAny.ts b/src/lualib/PromiseAny.ts index 7a368fe74..17c77f273 100644 --- a/src/lualib/PromiseAny.ts +++ b/src/lualib/PromiseAny.ts @@ -1,5 +1,5 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any -import { __TS__Promise, __TS__PromiseState } from "./Promise"; +import { __TS__Promise, PromiseState } from "./Promise"; // eslint-disable-next-line @typescript-eslint/promise-function-async export function __TS__PromiseAny(this: void, iterable: Iterable>): Promise { @@ -8,10 +8,10 @@ export function __TS__PromiseAny(this: void, iterable: Iterable(this: void, iterable: Iterable>): Promise { @@ -7,10 +7,10 @@ export function __TS__PromiseRace(this: void, iterable: Iterable = new Map(); - for (const file of files) { if (!file.luaAst) continue; const fileName = path.basename(file.fileName, ".ts"); @@ -24,7 +28,8 @@ export function createLuaLibModuleInfo(files: ProcessedFile[]): LuaLibModulesInf } } - const result: Partial = {}; + // lualibModulesInfo + const lualibModulesInfo: Partial = {}; for (const [feature, file] of luaLibFiles) { let dependencies: LuaLibFeature[] | undefined; const dependenciesForFeature = file.luaAst!.luaLibFeatures; @@ -35,33 +40,40 @@ export function createLuaLibModuleInfo(files: ProcessedFile[]): LuaLibModulesInf const exports = file.luaAst!.exports!; if (dependencies || exports) { - result[feature] = { + lualibModulesInfo[feature] = { dependencies, exports, }; } } + assume(lualibModulesInfo); - return result as LuaLibModulesInfo; -} - -export function createLuaLibBundle( - emitHost: EmitHost, - program: ts.Program, - luaLibModuleInfo: LuaLibModulesInfo -): string { - const allFeatures = Object.values(LuaLibFeature) as LuaLibFeature[]; - - let result = loadInlineLualibFeatures(allFeatures, emitHost); - - const exports = allFeatures.flatMap(feature => luaLibModuleInfo[feature].exports); + // lua bundle + const allFeatures = Array.from(luaLibFiles.keys()); + let lualibBundle = loadInlineLualibFeatures( + allFeatures, + emitHost, + lualibModulesInfo, + feature => luaLibFiles.get(feature)?.code ?? "" + ); + const exports = allFeatures.flatMap(feature => lualibModulesInfo[feature].exports); const statements: lua.TableFieldExpression[] = exports.map(exportName => lua.createTableFieldExpression(lua.createIdentifier(exportName), lua.createStringLiteral(exportName)) ); const moduleReturn = lua.createReturnStatement([lua.createTableExpression(statements)]); - const printer = new LuaPrinter(emitHost, program, "lualib_bundle.lua"); - result += `\n${printer.printReturnStatement(moduleReturn)}\n`; + lualibBundle += `\n${printer.printStatement(moduleReturn)}\n`; - return result; + return [ + { + code: lualibBundle, + fileName: "lualib_bundle.lua", + isRawFile: true, + }, + { + code: JSON.stringify(lualibModulesInfo, null, 2), + fileName: "lualib_dependencies.json", + isRawFile: true, + }, + ]; } diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index 90991f8d8..97bfc3446 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -7,7 +7,7 @@ import { isNonNull } from "../utils"; import { getPlugins, Plugin } from "./plugins"; import { getTransformers } from "./transformers"; import { EmitHost, ProcessedFile } from "./utils"; -import { createLuaLibModuleInfo, createLuaLibBundle } from "./lualib"; +import { generateExtraLualibFiles } from "./lualib"; export interface TranspileOptions { program: ts.Program; @@ -126,20 +126,7 @@ export function getProgramTranspileResult( if (options.luaLibProject) { // add lualib dependencies json file - const dependencyInfo = createLuaLibModuleInfo(transpiledFiles); - transpiledFiles.push({ - code: JSON.stringify(dependencyInfo, null, 2), - fileName: "lualib_dependencies.json", - isRawFile: true, - }); - - // add lualib bundle file - const bundle = createLuaLibBundle(emitHost, program, dependencyInfo); - transpiledFiles.push({ - code: bundle, - fileName: "lualib_bundle.lua", - isRawFile: true, - }); + transpiledFiles.push(...generateExtraLualibFiles(emitHost, program, transpiledFiles)); } options.noEmit = oldNoEmit; From 8ce23505a1f72ece22ab2755a836c31623bd2d1a Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 15:38:23 -0800 Subject: [PATCH 04/12] Add global assign test --- test/transpile/lualib.spec.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/transpile/lualib.spec.ts b/test/transpile/lualib.spec.ts index f05ffda2b..b35ab996b 100644 --- a/test/transpile/lualib.spec.ts +++ b/test/transpile/lualib.spec.ts @@ -1,6 +1,7 @@ import * as ts from "typescript"; import { LuaLibFeature } from "../../src"; import { readLuaLibFeature } from "../../src/LuaLib"; +import * as util from "../util"; test.each(Object.entries(LuaLibFeature))("Lualib does not use ____exports (%p)", (_, feature) => { const lualibCode = readLuaLibFeature(feature, ts.sys); @@ -8,3 +9,23 @@ test.each(Object.entries(LuaLibFeature))("Lualib does not use ____exports (%p)", const exportsOccurrences = lualibCode.match(/____exports/g); expect(exportsOccurrences).toBeNull(); }); + +test("Lualib bundle does not assign globals", () => { + // language=TypeScript + util.testModule` + declare const _G: LuaTable; + declare const require: (this: void, module: string) => any; + const globalKeys = new LuaTable(); + for (const [key] of _G) { + globalKeys[key] = true; + } + require("lualib_bundle"); + for (const [key] of _G) { + if (!globalKeys[key]) { + error("Global was assigned: " + key); + } + } + ` + .withLanguageExtensions() + .expectNoExecutionError(); +}); From 2d6141fc98998bf6668cb78bfea0dd81d4de4fc0 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 15:46:33 -0800 Subject: [PATCH 05/12] Use buildMode=lualib instead of luaLibProject compiler option --- src/CompilerOptions.ts | 2 +- src/cli/parse.ts | 5 ----- src/lualib/tsconfig.json | 4 ++-- src/transformation/utils/lua-ast.ts | 4 ++-- src/transformation/visitors/class/setup.ts | 3 ++- src/transformation/visitors/identifier.ts | 3 ++- src/transformation/visitors/modules/import.ts | 3 ++- src/transformation/visitors/sourceFile.ts | 3 ++- src/transpilation/transpile.ts | 4 ++-- 9 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index fde2cb593..382493c28 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -27,7 +27,6 @@ export type CompilerOptions = OmitIndexSignature & { luaBundleEntry?: string; luaTarget?: LuaTarget; luaLibImport?: LuaLibImportKind; - luaLibProject?: boolean; luaPlugins?: LuaPluginImport[]; noImplicitSelf?: boolean; noHeader?: boolean; @@ -57,6 +56,7 @@ export enum LuaTarget { export enum BuildMode { Default = "default", Library = "library", + LuaLib = "lualib", } export const isBundleEnabled = (options: CompilerOptions) => diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 4abb02fd8..37e809eb6 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -46,11 +46,6 @@ export const optionDeclarations: CommandLineOption[] = [ type: "enum", choices: Object.values(LuaLibImportKind), }, - { - name: "luaLibProject", - description: "If this project is lualib source.", - type: "boolean", - }, { name: "luaTarget", aliases: ["lt"], diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index ba21ec3a8..4931d9a7c 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -10,8 +10,8 @@ "noUnusedParameters": true }, "tstl": { + "buildMode": "lualib", "luaLibImport": "none", - "noHeader": true, - "luaLibProject": true + "noHeader": true } } diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index 3517bcfcc..c12ee49c1 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { LuaTarget } from "../../CompilerOptions"; +import { LuaTarget, BuildMode } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { assert, castArray } from "../../utils"; import { TransformationContext } from "../context"; @@ -146,7 +146,7 @@ export function createLocalOrExportedOrGlobalDeclaration( if (!rhs) { return []; } else { - if (context.options.luaLibProject) { + if (context.options.buildMode === BuildMode.LuaLib) { // assignment to a local variable defined elsewhere // see sourceFile.ts assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); diff --git a/src/transformation/visitors/class/setup.ts b/src/transformation/visitors/class/setup.ts index 36e9e7411..cd19cd02d 100644 --- a/src/transformation/visitors/class/setup.ts +++ b/src/transformation/visitors/class/setup.ts @@ -11,6 +11,7 @@ import { import { createExportsIdentifier, createLocalOrExportedOrGlobalDeclaration } from "../../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { getExtendedNode, getExtendsClause } from "./utils"; +import { BuildMode } from "../../../CompilerOptions"; export function createClassSetup( context: TransformationContext, @@ -40,7 +41,7 @@ export function createClassSetup( result.push(lua.createVariableDeclarationStatement(localClassName, defaultExportLeftHandSide)); } else { const exportScope = getIdentifierExportScope(context, className); - if (exportScope && !context.options.luaLibProject) { + if (exportScope && context.options.buildMode !== BuildMode.LuaLib) { // local localClassName = ____exports.className result.push( lua.createVariableDeclarationStatement( diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index b7e142c14..c48ae01bd 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -23,6 +23,7 @@ import { isTableExtensionIdentifier } from "./language-extensions/table"; import { isVarargConstantNode } from "./language-extensions/vararg"; import { isOptionalContinuation } from "./optional-chaining"; import { isErrorClass } from "../builtins/error"; +import { BuildMode } from "../../CompilerOptions"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { if (isOptionalContinuation(identifier)) { @@ -73,7 +74,7 @@ export function transformIdentifier(context: TransformationContext, identifier: export const transformIdentifierExpression: FunctionVisitor = (node, context) => { const symbol = context.checker.getSymbolAtLocation(node); - if (symbol && !context.options.luaLibProject) { + if (symbol && context.options.buildMode !== BuildMode.LuaLib) { const exportScope = getSymbolExportScope(context, symbol); if (exportScope) { const name = symbol.name; diff --git a/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index 8f755dd8d..ee0f0d470 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -12,6 +12,7 @@ import { transformIdentifier } from "../identifier"; import { transformPropertyName } from "../literal"; import { LuaLibFeature } from "../../../LuaLib"; import { importLuaLibFeature } from "../../utils/lualib"; +import { BuildMode } from "../../../CompilerOptions"; function isNoResolutionPath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { const moduleOwnerSymbol = context.checker.getSymbolAtLocation(moduleSpecifier); @@ -61,7 +62,7 @@ function transformImportSpecifier( } export const transformImportDeclaration: FunctionVisitor = (statement, context) => { - if (context.options.luaLibProject) { + if (context.options.buildMode === BuildMode.LuaLib) { // in lualib project, imports declare dependencies // Assume that relevant imports are in the form `import "./module"` diff --git a/src/transformation/visitors/sourceFile.ts b/src/transformation/visitors/sourceFile.ts index fdfdda425..2c6b4203e 100644 --- a/src/transformation/visitors/sourceFile.ts +++ b/src/transformation/visitors/sourceFile.ts @@ -10,6 +10,7 @@ import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope" import { hasExportEquals } from "../utils/typescript"; import { getSymbolIdOfSymbol, getSymbolInfo } from "../utils/symbols"; import { getExportedSymbolsFromScope } from "../utils/export"; +import { BuildMode } from "../../CompilerOptions"; export const transformSourceFileNode: FunctionVisitor = (node, context) => { let statements: lua.Statement[] = []; @@ -35,7 +36,7 @@ export const transformSourceFileNode: FunctionVisitor = (node, co statements = performHoisting(context, context.transformStatements(node.statements)); popScope(context); - if (context.options.luaLibProject) { + if (context.options.buildMode === BuildMode.LuaLib) { // Exports are currently assignment statements const exportedSymbolIds = getExportedSymbolsFromScope(context, context.sourceFile) .map(symbol => getSymbolIdOfSymbol(context, symbol)) diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index 97bfc3446..4d72bc014 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -1,6 +1,6 @@ import * as path from "path"; import * as ts from "typescript"; -import { CompilerOptions, validateOptions } from "../CompilerOptions"; +import { CompilerOptions, validateOptions, BuildMode } from "../CompilerOptions"; import { createPrinter } from "../LuaPrinter"; import { createVisitorMap, transformSourceFile } from "../transformation"; import { isNonNull } from "../utils"; @@ -124,7 +124,7 @@ export function getProgramTranspileResult( program.getSourceFiles().filter(isEmittableJsonFile).forEach(processSourceFile); } - if (options.luaLibProject) { + if (options.buildMode === BuildMode.LuaLib) { // add lualib dependencies json file transpiledFiles.push(...generateExtraLualibFiles(emitHost, program, transpiledFiles)); } From 9279d2762220275f9f41361a90f8f8f9cadac22a Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:19:28 -0800 Subject: [PATCH 06/12] Consistent naming for in LuaLibModulesInfo --- src/LuaLib.ts | 17 +++++++++-------- src/transpilation/lualib.ts | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/LuaLib.ts b/src/LuaLib.ts index 4cacb5bbc..54e19a428 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -106,18 +106,19 @@ export interface LuaLibFeatureInfo { } export type LuaLibModulesInfo = Record; -let luaLibModuleInfo: LuaLibModulesInfo | undefined; -export function getLuaLibModuleInfo(emitHost: EmitHost): LuaLibModulesInfo { - if (luaLibModuleInfo === undefined) { - const lualibPath = path.resolve(__dirname, "../dist/lualib/lualib_dependencies.json"); +export const luaLibModulesInfoFileName = "lualib_module_info.json"; +let luaLibModulesInfo: LuaLibModulesInfo | undefined; +export function getLuaLibModulesInfo(emitHost: EmitHost): LuaLibModulesInfo { + if (luaLibModulesInfo === undefined) { + const lualibPath = path.resolve(__dirname, `../dist/lualib/${luaLibModulesInfoFileName}`); const result = emitHost.readFile(lualibPath); if (result !== undefined) { - luaLibModuleInfo = JSON.parse(result) as LuaLibModulesInfo; + luaLibModulesInfo = JSON.parse(result) as LuaLibModulesInfo; } else { throw new Error(`Could not load lualib dependencies from '${lualibPath}'`); } } - return luaLibModuleInfo; + return luaLibModulesInfo; } export function readLuaLibFeature(feature: LuaLibFeature, emitHost: EmitHost): string { @@ -132,7 +133,7 @@ export function readLuaLibFeature(feature: LuaLibFeature, emitHost: EmitHost): s export function loadInlineLualibFeatures( features: Iterable, emitHost: EmitHost, - luaLibModulesInfo: LuaLibModulesInfo = getLuaLibModuleInfo(emitHost), + luaLibModulesInfo: LuaLibModulesInfo = getLuaLibModulesInfo(emitHost), readFeature: (feature: LuaLibFeature) => string = feature => readLuaLibFeature(feature, emitHost) ): string { let result = ""; @@ -163,7 +164,7 @@ export function loadImportedLualibFeatures( emitHost: EmitHost, alwaysRequire = false ): lua.Statement[] { - const luaLibModuleInfo = getLuaLibModuleInfo(emitHost); + const luaLibModuleInfo = getLuaLibModulesInfo(emitHost); const imports = Array.from(features).flatMap(feature => luaLibModuleInfo[feature].exports); diff --git a/src/transpilation/lualib.ts b/src/transpilation/lualib.ts index 474a50e60..ddce14615 100644 --- a/src/transpilation/lualib.ts +++ b/src/transpilation/lualib.ts @@ -1,5 +1,5 @@ import { ProcessedFile, EmitHost } from "./utils"; -import { LuaLibFeature, LuaLibModulesInfo, loadInlineLualibFeatures } from "../LuaLib"; +import { LuaLibFeature, LuaLibModulesInfo, loadInlineLualibFeatures, luaLibModulesInfoFileName } from "../LuaLib"; import * as path from "path"; import { LuaPrinter } from "../LuaPrinter"; import * as lua from "../LuaAST"; @@ -72,7 +72,7 @@ export function generateExtraLualibFiles( }, { code: JSON.stringify(lualibModulesInfo, null, 2), - fileName: "lualib_dependencies.json", + fileName: luaLibModulesInfoFileName, isRawFile: true, }, ]; From 323bced577aa4068dad456b721402bb5fdf80cd0 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 7 Feb 2022 09:20:49 -0800 Subject: [PATCH 07/12] Remove now unnecessary imports from build-lualib.js --- build-lualib.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/build-lualib.js b/build-lualib.js index 8a35f147b..5b82a0b57 100644 --- a/build-lualib.js +++ b/build-lualib.js @@ -1,9 +1,6 @@ require("ts-node/register/transpile-only"); -const fs = require("fs"); const path = require("path"); -const ts = require("typescript"); const tstl = require("./src"); -const { loadInlineLualibFeatures } = require("./src/LuaLib"); const configFileName = path.resolve(__dirname, "src/lualib/tsconfig.json"); const { diagnostics } = tstl.transpileProject(configFileName); From aceb355d63e368868f58917b4e813901e9b74276 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:18:23 -0800 Subject: [PATCH 08/12] Make lualib compilation a separate, internal option --- src/CompilerOptions.ts | 2 +- src/cli/information.ts | 1 + src/cli/parse.ts | 9 ++++++++- src/lualib/tsconfig.json | 5 ++--- src/transformation/utils/lua-ast.ts | 4 ++-- src/transformation/visitors/class/setup.ts | 3 +-- src/transformation/visitors/identifier.ts | 3 +-- src/transformation/visitors/modules/import.ts | 3 +-- src/transformation/visitors/sourceFile.ts | 3 +-- src/transpilation/transpile.ts | 4 ++-- 10 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 382493c28..a107f5251 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -34,6 +34,7 @@ export type CompilerOptions = OmitIndexSignature & { plugins?: Array; sourceMapTraceback?: boolean; tstlVerbose?: boolean; + luaLibCompilation?: boolean; [option: string]: any; }; @@ -56,7 +57,6 @@ export enum LuaTarget { export enum BuildMode { Default = "default", Library = "library", - LuaLib = "lualib", } export const isBundleEnabled = (options: CompilerOptions) => diff --git a/src/cli/information.ts b/src/cli/information.ts index 4c4b3ce1a..f2420606b 100644 --- a/src/cli/information.ts +++ b/src/cli/information.ts @@ -19,6 +19,7 @@ export function getHelpString(): string { result += "Options:\n"; for (const option of optionDeclarations) { + if (option.internal) continue; const aliasStrings = (option.aliases ?? []).map(a => "-" + a); const optionString = [...aliasStrings, "--" + option.name].join("|"); diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 37e809eb6..44731aa0a 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -10,6 +10,7 @@ interface CommandLineOptionBase { name: string; aliases?: string[]; description: string; + internal?: boolean; } interface CommandLineOptionOfEnum extends CommandLineOptionBase { @@ -83,6 +84,12 @@ export const optionDeclarations: CommandLineOption[] = [ description: "An array of paths that tstl should not resolve and keep as-is.", type: "array", }, + { + name: "luaLibCompilation", + description: "Internal. Specifies if this project is lualib source.", + type: "boolean", + internal: true, + }, ]; export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine { @@ -103,7 +110,7 @@ export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): for (const [name, rawValue] of Object.entries(parsedConfigFile.raw.tstl)) { const option = optionDeclarations.find(option => option.name === name); - if (!option) { + if (!option && option !== "lualibCompilation") { parsedConfigFile.errors.push(cliDiagnostics.unknownCompilerOption(name)); continue; } diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index 4931d9a7c..9ea3e1e94 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -5,13 +5,12 @@ "lib": ["esnext"], "types": ["lua-types/5.4"], "skipLibCheck": true, - "noUnusedLocals": true, "noUnusedParameters": true }, "tstl": { - "buildMode": "lualib", "luaLibImport": "none", - "noHeader": true + "noHeader": true, + "luaLibCompilation": true } } diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index c12ee49c1..f92bb95d4 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { LuaTarget, BuildMode } from "../../CompilerOptions"; +import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { assert, castArray } from "../../utils"; import { TransformationContext } from "../context"; @@ -146,7 +146,7 @@ export function createLocalOrExportedOrGlobalDeclaration( if (!rhs) { return []; } else { - if (context.options.buildMode === BuildMode.LuaLib) { + if (context.options.luaLibCompilation) { // assignment to a local variable defined elsewhere // see sourceFile.ts assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); diff --git a/src/transformation/visitors/class/setup.ts b/src/transformation/visitors/class/setup.ts index cd19cd02d..355f29c55 100644 --- a/src/transformation/visitors/class/setup.ts +++ b/src/transformation/visitors/class/setup.ts @@ -11,7 +11,6 @@ import { import { createExportsIdentifier, createLocalOrExportedOrGlobalDeclaration } from "../../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { getExtendedNode, getExtendsClause } from "./utils"; -import { BuildMode } from "../../../CompilerOptions"; export function createClassSetup( context: TransformationContext, @@ -41,7 +40,7 @@ export function createClassSetup( result.push(lua.createVariableDeclarationStatement(localClassName, defaultExportLeftHandSide)); } else { const exportScope = getIdentifierExportScope(context, className); - if (exportScope && context.options.buildMode !== BuildMode.LuaLib) { + if (exportScope && !context.options.luaLibCompilation) { // local localClassName = ____exports.className result.push( lua.createVariableDeclarationStatement( diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index c48ae01bd..fdf70824e 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -23,7 +23,6 @@ import { isTableExtensionIdentifier } from "./language-extensions/table"; import { isVarargConstantNode } from "./language-extensions/vararg"; import { isOptionalContinuation } from "./optional-chaining"; import { isErrorClass } from "../builtins/error"; -import { BuildMode } from "../../CompilerOptions"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { if (isOptionalContinuation(identifier)) { @@ -74,7 +73,7 @@ export function transformIdentifier(context: TransformationContext, identifier: export const transformIdentifierExpression: FunctionVisitor = (node, context) => { const symbol = context.checker.getSymbolAtLocation(node); - if (symbol && context.options.buildMode !== BuildMode.LuaLib) { + if (symbol && !context.options.luaLibCompilation) { const exportScope = getSymbolExportScope(context, symbol); if (exportScope) { const name = symbol.name; diff --git a/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index ee0f0d470..f7d5fd7d6 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -12,7 +12,6 @@ import { transformIdentifier } from "../identifier"; import { transformPropertyName } from "../literal"; import { LuaLibFeature } from "../../../LuaLib"; import { importLuaLibFeature } from "../../utils/lualib"; -import { BuildMode } from "../../../CompilerOptions"; function isNoResolutionPath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { const moduleOwnerSymbol = context.checker.getSymbolAtLocation(moduleSpecifier); @@ -62,7 +61,7 @@ function transformImportSpecifier( } export const transformImportDeclaration: FunctionVisitor = (statement, context) => { - if (context.options.buildMode === BuildMode.LuaLib) { + if (context.options.luaLibCompilation) { // in lualib project, imports declare dependencies // Assume that relevant imports are in the form `import "./module"` diff --git a/src/transformation/visitors/sourceFile.ts b/src/transformation/visitors/sourceFile.ts index 2c6b4203e..5e42d3472 100644 --- a/src/transformation/visitors/sourceFile.ts +++ b/src/transformation/visitors/sourceFile.ts @@ -10,7 +10,6 @@ import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope" import { hasExportEquals } from "../utils/typescript"; import { getSymbolIdOfSymbol, getSymbolInfo } from "../utils/symbols"; import { getExportedSymbolsFromScope } from "../utils/export"; -import { BuildMode } from "../../CompilerOptions"; export const transformSourceFileNode: FunctionVisitor = (node, context) => { let statements: lua.Statement[] = []; @@ -36,7 +35,7 @@ export const transformSourceFileNode: FunctionVisitor = (node, co statements = performHoisting(context, context.transformStatements(node.statements)); popScope(context); - if (context.options.buildMode === BuildMode.LuaLib) { + if (context.options.luaLibCompilation) { // Exports are currently assignment statements const exportedSymbolIds = getExportedSymbolsFromScope(context, context.sourceFile) .map(symbol => getSymbolIdOfSymbol(context, symbol)) diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index 4d72bc014..01923c40e 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -1,6 +1,6 @@ import * as path from "path"; import * as ts from "typescript"; -import { CompilerOptions, validateOptions, BuildMode } from "../CompilerOptions"; +import { CompilerOptions, validateOptions } from "../CompilerOptions"; import { createPrinter } from "../LuaPrinter"; import { createVisitorMap, transformSourceFile } from "../transformation"; import { isNonNull } from "../utils"; @@ -124,7 +124,7 @@ export function getProgramTranspileResult( program.getSourceFiles().filter(isEmittableJsonFile).forEach(processSourceFile); } - if (options.buildMode === BuildMode.LuaLib) { + if (options.luaLibCompilation) { // add lualib dependencies json file transpiledFiles.push(...generateExtraLualibFiles(emitHost, program, transpiledFiles)); } From db0e257515c5ac44f7738d08cc7f871bf4091ebd Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:18:48 -0800 Subject: [PATCH 09/12] Add lualib_modules_info.json to npm package --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0db3f1280..d32b6e4ab 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "dist/**/*.js", "dist/**/*.lua", "dist/**/*.ts", - "language-extensions/**/*.ts" + "language-extensions/**/*.ts", + "dist/lualib/*.json" ], "main": "dist/index.js", "types": "dist/index.d.ts", From 8d5283c31e0f7f0dc3dcafb96410a4388af94e7c Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 7 Feb 2022 19:22:59 -0800 Subject: [PATCH 10/12] Move checkForLualibType to identifier --- src/transformation/builtins/error.ts | 18 --------- src/transformation/builtins/index.ts | 45 ++++++++++++++++++++- src/transformation/visitors/class/index.ts | 7 +--- src/transformation/visitors/class/new.ts | 46 +--------------------- src/transformation/visitors/identifier.ts | 10 ++--- test/unit/builtins/set.spec.ts | 7 ++++ 6 files changed, 58 insertions(+), 75 deletions(-) delete mode 100644 src/transformation/builtins/error.ts diff --git a/src/transformation/builtins/error.ts b/src/transformation/builtins/error.ts deleted file mode 100644 index 84b9bb314..000000000 --- a/src/transformation/builtins/error.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TransformationContext } from "../context"; -import * as ts from "typescript"; -import { isStandardLibraryType } from "../utils/typescript"; - -const errorClasses = new Set([ - "Error", - "EvalError", - "RangeError", - "ReferenceError", - "SyntaxError", - "TypeError", - "URIError", -]); - -export function isErrorClass(context: TransformationContext, node: ts.Identifier) { - const type = context.checker.getTypeAtLocation(node); - return isStandardLibraryType(context, type, undefined) && errorClasses.has(node.text); -} diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index dee199f7b..256ee4eda 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -14,7 +14,6 @@ import { isStringType, } from "../utils/typescript"; import { PropertyCallExpression } from "../visitors/call"; -import { checkForLuaLibType } from "../visitors/class/new"; import { transformArrayConstructorCall, transformArrayProperty, transformArrayPrototypeCall } from "./array"; import { transformConsoleCall } from "./console"; import { transformFunctionPrototypeCall, transformFunctionProperty } from "./function"; @@ -67,7 +66,6 @@ export function transformBuiltinCallExpression( const expressionType = context.checker.getTypeAtLocation(node.expression); if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, expressionType, undefined)) { // TODO: - checkForLuaLibType(context, expressionType); const result = transformGlobalCall(context, node); if (result) { if (isOptionalCall) return unsupportedOptionalCall(); @@ -159,3 +157,46 @@ export function transformBuiltinIdentifierExpression( return lua.createIdentifier("_G", node, getIdentifierSymbolId(context, node), "globalThis"); } } + +const builtinErrorTypeNames = new Set([ + "Error", + "ErrorConstructor", + "RangeError", + "RangeErrorConstructor", + "ReferenceError", + "ReferenceErrorConstructor", + "SyntaxError", + "SyntaxErrorConstructor", + "TypeError", + "TypeErrorConstructor", + "URIError", + "URIErrorConstructor", +]); + +export function checkForLuaLibType(context: TransformationContext, type: ts.Type): void { + if (!type.symbol) return; + + const name = context.checker.getFullyQualifiedName(type.symbol); + switch (name) { + case "Map": + case "MapConstructor": + importLuaLibFeature(context, LuaLibFeature.Map); + return; + case "Set": + case "SetConstructor": + importLuaLibFeature(context, LuaLibFeature.Set); + return; + case "WeakMap": + case "WeakMapConstructor": + importLuaLibFeature(context, LuaLibFeature.WeakMap); + return; + case "WeakSet": + case "WeakSetConstructor": + importLuaLibFeature(context, LuaLibFeature.WeakSet); + return; + } + + if (builtinErrorTypeNames.has(name)) { + importLuaLibFeature(context, LuaLibFeature.Error); + } +} diff --git a/src/transformation/visitors/class/index.ts b/src/transformation/visitors/class/index.ts index f43b349e5..5257c2db6 100644 --- a/src/transformation/visitors/class/index.ts +++ b/src/transformation/visitors/class/index.ts @@ -23,9 +23,8 @@ import { transformStaticPropertyDeclaration, } from "./members/fields"; import { createMethodDecoratingExpression, transformMethodDeclaration } from "./members/method"; -import { checkForLuaLibType } from "./new"; -import { createClassSetup } from "./setup"; import { getExtendedNode, getExtendedType, isStaticNode } from "./utils"; +import { createClassSetup } from "./setup"; export const transformClassDeclaration: FunctionVisitor = (declaration, context) => { // If declaration is a default export, transform to export variable assignment instead @@ -86,10 +85,6 @@ function transformClassLikeDeclaration( const superInfo = getOrUpdate(classSuperInfos, context, () => []); superInfo.push({ className, extendedTypeNode }); - if (extendedType) { - checkForLuaLibType(context, extendedType); - } - // Get all properties with value const properties = classDeclaration.members.filter(ts.isPropertyDeclaration).filter(member => member.initializer); diff --git a/src/transformation/visitors/class/new.ts b/src/transformation/visitors/class/new.ts index f7433e7b5..13a4a57a9 100644 --- a/src/transformation/visitors/class/new.ts +++ b/src/transformation/visitors/class/new.ts @@ -1,52 +1,12 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { FunctionVisitor, TransformationContext } from "../../context"; +import { FunctionVisitor } from "../../context"; import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations"; import { annotationInvalidArgumentCount, annotationRemoved } from "../../utils/diagnostics"; -import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; +import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { transformArguments, transformCallAndArguments } from "../call"; import { isTableNewCall } from "../language-extensions/table"; -const builtinErrorTypeNames = new Set([ - "Error", - "ErrorConstructor", - "RangeError", - "RangeErrorConstructor", - "ReferenceError", - "ReferenceErrorConstructor", - "SyntaxError", - "SyntaxErrorConstructor", - "TypeError", - "TypeErrorConstructor", - "URIError", - "URIErrorConstructor", -]); - -// TODO: Do it in identifier? -export function checkForLuaLibType(context: TransformationContext, type: ts.Type): void { - if (!type.symbol) return; - - const name = context.checker.getFullyQualifiedName(type.symbol); - switch (name) { - case "Map": - importLuaLibFeature(context, LuaLibFeature.Map); - return; - case "Set": - importLuaLibFeature(context, LuaLibFeature.Set); - return; - case "WeakMap": - importLuaLibFeature(context, LuaLibFeature.WeakMap); - return; - case "WeakSet": - importLuaLibFeature(context, LuaLibFeature.WeakSet); - return; - } - - if (builtinErrorTypeNames.has(name)) { - importLuaLibFeature(context, LuaLibFeature.Error); - } -} - export const transformNewExpression: FunctionVisitor = (node, context) => { const type = context.checker.getTypeAtLocation(node); @@ -68,8 +28,6 @@ export const transformNewExpression: FunctionVisitor = (node, signature ); - checkForLuaLibType(context, type); - const customConstructorAnnotation = annotations.get(AnnotationKind.CustomConstructor); if (customConstructorAnnotation) { if (customConstructorAnnotation.args.length === 1) { diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index fdf70824e..05543ead7 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -1,6 +1,6 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { transformBuiltinIdentifierExpression } from "../builtins"; +import { transformBuiltinIdentifierExpression, checkForLuaLibType } from "../builtins"; import { isPromiseClass } from "../builtins/promise"; import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context"; import { AnnotationKind, isForRangeType } from "../utils/annotations"; @@ -22,7 +22,7 @@ import { isRangeFunctionNode } from "./language-extensions/range"; import { isTableExtensionIdentifier } from "./language-extensions/table"; import { isVarargConstantNode } from "./language-extensions/vararg"; import { isOptionalContinuation } from "./optional-chaining"; -import { isErrorClass } from "../builtins/error"; +import { isStandardLibraryType } from "../utils/typescript"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { if (isOptionalContinuation(identifier)) { @@ -60,9 +60,9 @@ export function transformIdentifier(context: TransformationContext, identifier: importLuaLibFeature(context, LuaLibFeature.Promise); return lua.createIdentifier("__TS__Promise", identifier); } - - if (isErrorClass(context, identifier)) { - importLuaLibFeature(context, LuaLibFeature.Error); + const type = context.checker.getTypeAtLocation(identifier); + if (isStandardLibraryType(context, type, undefined)) { + checkForLuaLibType(context, type); } const text = hasUnsafeIdentifierName(context, identifier) ? createSafeName(identifier.text) : identifier.text; diff --git a/test/unit/builtins/set.spec.ts b/test/unit/builtins/set.spec.ts index 8f81edfe8..93e23e98e 100644 --- a/test/unit/builtins/set.spec.ts +++ b/test/unit/builtins/set.spec.ts @@ -194,3 +194,10 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM `.expectToMatchJsResult(); }); }); + +test("instanceof Set without creating set", () => { + util.testFunction` + const myset = 3 as any; + return myset instanceof Set; + `.expectToMatchJsResult(); +}); From c5e3e9e9970fd71454a4b0e0bd164c0a383b4ca9 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Sun, 27 Feb 2022 11:06:07 -0800 Subject: [PATCH 11/12] PR feedback --- src/LuaPrinter.ts | 2 +- src/transpilation/bundle.ts | 1 - src/transpilation/transpiler.ts | 13 +++++-------- test/transpile/module-resolution.spec.ts | 3 ++- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index d73c52148..b1d997a6b 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -173,7 +173,7 @@ export class LuaPrinter { public print(file: lua.File): PrintResult { // Add traceback lualib if sourcemap traceback option is enabled - if (this.options.sourceMapTraceback && !isBundleEnabled(this.options)) { + if (this.options.sourceMapTraceback) { file.luaLibFeatures.add(LuaLibFeature.SourceMapTraceBack); } diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 3b9a1287a..0c039ad4f 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -104,7 +104,6 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const footers: string[] = []; if (options.sourceMapTraceback) { // Generates SourceMapTraceback for the entire file - // TODO: remove footers.push('local __TS__SourceMapTraceBack = require("lualib_bundle").__TS__SourceMapTraceBack\n'); footers.push(`${sourceMapTracebackBundlePlaceholder}\n`); } diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 79e4eaf29..442d79e27 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -100,7 +100,9 @@ export class Transpiler { } else { emitPlan = resolutionResult.resolvedFiles.map(file => ({ ...file, - outputPath: getEmitPath(file, program), + outputPath: file.isRawFile + ? path.join(getEmitOutDir(program), file.fileName) + : getEmitPath(file.fileName, program), })); } @@ -108,14 +110,9 @@ export class Transpiler { } } -export function getEmitPath(file: string | ProcessedFile, program: ts.Program): string { +export function getEmitPath(file: string, program: ts.Program): string { const outDir = getEmitOutDir(program); - if (typeof file !== "string" && file.isRawFile) { - return path.join(outDir, file.fileName); - } - const fileName = typeof file === "string" ? file : file.fileName; - const relativeOutputPath = getEmitPathRelativeToOutDir(fileName, program); - + const relativeOutputPath = getEmitPathRelativeToOutDir(file, program); return path.join(outDir, relativeOutputPath); } diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 244432ead..477426c72 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -490,7 +490,8 @@ test("includes lualib_bundle when external lua requests it", () => { .addExtraFile( "lualibuser.lua", ` - local __TS__ArrayPush = require("lualib_bundle").__TS__ArrayPush + local ____lualib = require("lualib_bundle") + local __TS__ArrayPush = ____lualib.__TS__ArrayPush local result = {} __TS__ArrayPush(result, "foo") From 1132f1692b3ebc2fe84b81d5b4bf25acb6e7c375 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Sun, 27 Feb 2022 12:04:31 -0800 Subject: [PATCH 12/12] Move lualibCompilation option out of public api --- build-lualib.js | 2 +- src/CompilerOptions.ts | 1 + src/cli/information.ts | 1 - src/cli/parse.ts | 9 +-------- src/lualib/tsconfig.json | 3 +-- src/transpilation/index.ts | 11 +++++++++++ 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/build-lualib.js b/build-lualib.js index 5b82a0b57..66d8cb485 100644 --- a/build-lualib.js +++ b/build-lualib.js @@ -3,5 +3,5 @@ const path = require("path"); const tstl = require("./src"); const configFileName = path.resolve(__dirname, "src/lualib/tsconfig.json"); -const { diagnostics } = tstl.transpileProject(configFileName); +const { diagnostics } = tstl.transpileLuaLibProject(configFileName); diagnostics.forEach(tstl.createDiagnosticReporter(true)); diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index a107f5251..1711031e8 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -34,6 +34,7 @@ export type CompilerOptions = OmitIndexSignature & { plugins?: Array; sourceMapTraceback?: boolean; tstlVerbose?: boolean; + /** @internal */ luaLibCompilation?: boolean; [option: string]: any; }; diff --git a/src/cli/information.ts b/src/cli/information.ts index f2420606b..4c4b3ce1a 100644 --- a/src/cli/information.ts +++ b/src/cli/information.ts @@ -19,7 +19,6 @@ export function getHelpString(): string { result += "Options:\n"; for (const option of optionDeclarations) { - if (option.internal) continue; const aliasStrings = (option.aliases ?? []).map(a => "-" + a); const optionString = [...aliasStrings, "--" + option.name].join("|"); diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 44731aa0a..37e809eb6 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -10,7 +10,6 @@ interface CommandLineOptionBase { name: string; aliases?: string[]; description: string; - internal?: boolean; } interface CommandLineOptionOfEnum extends CommandLineOptionBase { @@ -84,12 +83,6 @@ export const optionDeclarations: CommandLineOption[] = [ description: "An array of paths that tstl should not resolve and keep as-is.", type: "array", }, - { - name: "luaLibCompilation", - description: "Internal. Specifies if this project is lualib source.", - type: "boolean", - internal: true, - }, ]; export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine { @@ -110,7 +103,7 @@ export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): for (const [name, rawValue] of Object.entries(parsedConfigFile.raw.tstl)) { const option = optionDeclarations.find(option => option.name === name); - if (!option && option !== "lualibCompilation") { + if (!option) { parsedConfigFile.errors.push(cliDiagnostics.unknownCompilerOption(name)); continue; } diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index 9ea3e1e94..ecaf27419 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -10,7 +10,6 @@ }, "tstl": { "luaLibImport": "none", - "noHeader": true, - "luaLibCompilation": true + "noHeader": true } } diff --git a/src/transpilation/index.ts b/src/transpilation/index.ts index 59e83233a..46e76e42d 100644 --- a/src/transpilation/index.ts +++ b/src/transpilation/index.ts @@ -40,6 +40,17 @@ export function transpileProject( return transpileFiles(parseResult.fileNames, parseResult.options, writeFile); } +/** @internal */ +export function transpileLuaLibProject(configFileName: string): EmitResult { + const parseResult = parseConfigFileWithSystem(configFileName); + if (parseResult.errors.length > 0) { + return { diagnostics: parseResult.errors, emitSkipped: true }; + } + parseResult.options.luaLibCompilation = true; + + return transpileFiles(parseResult.fileNames, parseResult.options); +} + const libCache: { [key: string]: ts.SourceFile } = {}; /** @internal */