diff --git a/build-lualib.js b/build-lualib.js deleted file mode 100644 index 41ece7e32..000000000 --- a/build-lualib.js +++ /dev/null @@ -1,17 +0,0 @@ -require("ts-node/register/transpile-only"); -const fs = require("fs"); -const path = require("path"); -const ts = require("typescript"); -const tstl = require("./src"); -const { loadLuaLibFeatures } = 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/package.json b/package.json index 320b3d17b..ae73f35b6 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,14 @@ "dist/**/*.js", "dist/**/*.lua", "dist/**/*.ts", + "dist/lualib/*.json", "language-extensions/**/*.ts" ], "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsc && npm run build-lualib", - "build-lualib": "node build-lualib.js", + "build-lualib": "node dist/tstl.js -p src/lualib/tsconfig.json", "pretest": "npm run lint && npm run check:language-extensions && npm run build-lualib", "test": "jest", "lint": "npm run lint:eslint && npm run lint:prettier", diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index af9a9a75a..1e1ba8c72 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -40,7 +40,6 @@ export type CompilerOptions = OmitIndexSignature & { export enum LuaLibImportKind { None = "none", - Always = "always", Inline = "inline", Require = "require", } diff --git a/src/LuaLib.ts b/src/LuaLib.ts index e4321d181..e5df8927a 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,54 @@ 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 { - let result = ""; +export interface LuaLibFeatureInfo { + dependencies?: LuaLibFeature[]; + exports: string[]; +} +export type LuaLibModulesInfo = Record; + +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) { + luaLibModulesInfo = JSON.parse(result) as LuaLibModulesInfo; + } else { + throw new Error(`Could not load lualib dependencies from '${lualibPath}'`); + } + } + return luaLibModulesInfo; +} +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 resolveRecursiveLualibFeatures( + features: Iterable, + emitHost: EmitHost, + luaLibModulesInfo: LuaLibModulesInfo = getLuaLibModulesInfo(emitHost) +): LuaLibFeature[] { const loadedFeatures = new Set(); + const result: LuaLibFeature[] = []; function load(feature: LuaLibFeature): void { if (loadedFeatures.has(feature)) return; loadedFeatures.add(feature); - const dependencies = luaLibDependencies[feature]; + const dependencies = luaLibModulesInfo[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}'`); - } + result.push(feature); } for (const feature of features) { @@ -183,6 +157,44 @@ export function loadLuaLibFeatures(features: Iterable, emitHost: return result; } +export function loadInlineLualibFeatures(features: Iterable, emitHost: EmitHost): string { + let result = ""; + + for (const feature of resolveRecursiveLualibFeatures(features, emitHost)) { + const luaLibFeature = readLuaLibFeature(feature, emitHost); + result += luaLibFeature + "\n"; + } + + return result; +} + +export function loadImportedLualibFeatures(features: Iterable, emitHost: EmitHost): lua.Statement[] { + const luaLibModuleInfo = getLuaLibModulesInfo(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) { + 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..cc8a6fa46 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 @@ -233,18 +232,18 @@ export class LuaPrinter { if (!this.options.noHeader) { header += tstlHeader; } + let statements = file.statements; const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require; - if ( - luaLibImport === LuaLibImportKind.Always || - (luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0) - ) { - // Require lualib bundle - header += 'require("lualib_bundle");\n'; + if (luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0) { + // Import lualib features + const importStatements = loadImportedLualibFeatures(file.luaLibFeatures, this.emitHost); + + statements = importStatements.concat(statements); } 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)) { @@ -253,7 +252,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/index.ts b/src/index.ts index 3d060a994..0b994c7f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,3 +8,4 @@ export { LuaLibFeature } from "./LuaLib"; export * from "./LuaPrinter"; export * from "./transformation/context"; export * from "./transpilation"; +export { ProcessedFile } from "./transpilation/utils"; diff --git a/src/lualib-build/plugin.ts b/src/lualib-build/plugin.ts new file mode 100644 index 000000000..a14c8917a --- /dev/null +++ b/src/lualib-build/plugin.ts @@ -0,0 +1,173 @@ +import { SourceNode } from "source-map"; +import * as ts from "typescript"; +import * as tstl from ".."; +import * as path from "path"; +import { getUsedLuaLibFeatures } from "../transformation/utils/lualib"; +import { LuaLibFeature, LuaLibModulesInfo, luaLibModulesInfoFileName, resolveRecursiveLualibFeatures } from "../LuaLib"; +import { EmitHost, ProcessedFile } from "../transpilation/utils"; +import { + isExportAlias, + isExportAssignment, + isExportsReturn, + isExportTableDeclaration, + isImport, + isRequire, +} from "./util"; +import { createDiagnosticFactoryWithCode } from "../utils"; + +export const lualibDiagnostic = createDiagnosticFactoryWithCode(200000, (message: string, file?: ts.SourceFile) => ({ + messageText: message, + file, + start: file && 0, + length: file && 0, +})); + +class LuaLibPlugin implements tstl.Plugin { + // Plugin members + public visitors = { + [ts.SyntaxKind.SourceFile]: this.lualibFileVisitor.bind(this), + }; + + public printer: tstl.Printer = (program, emitHost, fileName, file) => + new LuaLibPrinter(emitHost, program, fileName).print(file); + + public afterPrint(program: ts.Program, options: tstl.CompilerOptions, emitHost: EmitHost, result: ProcessedFile[]) { + void options; + + // Write lualib dependency json + const { result: luaLibModuleInfo, diagnostics } = this.createLuaLibModulesInfo(); + const emitBOM = options.emitBOM ?? false; + emitHost.writeFile( + path.join(tstl.getEmitOutDir(program), luaLibModulesInfoFileName), + JSON.stringify(luaLibModuleInfo, null, 2), + emitBOM + ); + + // Create map of result files keyed by their 'lualib name' + const exportedLualibFeatures = new Map(result.map(f => [path.basename(f.fileName).split(".")[0], f.code])); + + // Figure out the order required in the bundle by recursively resolving all dependency features + const allFeatures = Object.values(LuaLibFeature) as LuaLibFeature[]; + const orderedFeatures = resolveRecursiveLualibFeatures(allFeatures, emitHost, luaLibModuleInfo); + + // Concatenate lualib files into bundle with exports table and add lualib_bundle.lua to results + let lualibBundle = orderedFeatures.map(f => exportedLualibFeatures.get(LuaLibFeature[f])).join("\n"); + const exports = allFeatures.flatMap(feature => luaLibModuleInfo[feature].exports); + lualibBundle += `\nreturn {\n${exports.map(exportName => ` ${exportName} = ${exportName}`).join(",\n")}\n}\n`; + result.push({ fileName: "lualib_bundle.lua", code: lualibBundle }); + + return diagnostics; + } + + // Internals + protected featureExports: Map> = new Map(); + protected featureDependencies: Map> = new Map(); + + protected lualibFileVisitor(file: ts.SourceFile, context: tstl.TransformationContext): tstl.File { + const featureName = path.basename(file.fileName, ".ts") as tstl.LuaLibFeature; + if (!(featureName in tstl.LuaLibFeature)) { + context.diagnostics.push(lualibDiagnostic(`File is not a lualib feature: ${featureName}`, file)); + } + + // Transpile file as normal with tstl + const fileResult = context.superTransformNode(file)[0] as tstl.File; + + const usedFeatures = new Set(getUsedLuaLibFeatures(context)); + + // Get all imports in file + const importNames = new Set(); + const imports = file.statements.filter(ts.isImportDeclaration); + for (const { importClause, moduleSpecifier } of imports) { + if (importClause?.namedBindings && ts.isNamedImports(importClause.namedBindings)) { + for (const { name } of importClause.namedBindings.elements) { + importNames.add(name.text); + } + } + // track lualib imports + if (ts.isStringLiteral(moduleSpecifier)) { + const featureName = path.basename(moduleSpecifier.text, ".ts") as tstl.LuaLibFeature; + if (featureName in tstl.LuaLibFeature) { + usedFeatures.add(featureName); + } + } + } + + const filteredStatements = fileResult.statements + .filter( + s => !isExportTableDeclaration(s) && !isRequire(s) && !isImport(s, importNames) && !isExportsReturn(s) + ) + .map(statement => { + if (isExportAlias(statement)) { + const name = statement.left[0]; + const exportName = statement.right[0].index.value; + if (name.text === exportName) return undefined; // Remove "x = x" statements + return tstl.createAssignmentStatement(name, tstl.createIdentifier(exportName)); + } + return statement; + }) + .filter(statement => statement !== undefined) as tstl.Statement[]; + + const exportNames = filteredStatements.filter(isExportAssignment).map(s => s.left[0].index.value); + if (!filteredStatements.every(isExportAssignment)) { + // If there are local statements, wrap them in a do ... end with exports outside + const exports = tstl.createVariableDeclarationStatement(exportNames.map(k => tstl.createIdentifier(k))); + // transform export assignments to local assignments + const bodyStatements = filteredStatements.map(s => + isExportAssignment(s) + ? tstl.createAssignmentStatement(tstl.createIdentifier(s.left[0].index.value), s.right[0]) + : s + ); + + fileResult.statements = [exports, tstl.createDoStatement(bodyStatements)]; + } else { + // transform export assignments to local variable declarations + fileResult.statements = filteredStatements.map(s => + tstl.createVariableDeclarationStatement(tstl.createIdentifier(s.left[0].index.value), s.right[0]) + ); + } + + // Save dependency information + this.featureExports.set(featureName, new Set(exportNames)); + if (usedFeatures.size > 0) { + this.featureDependencies.set(featureName, usedFeatures); + } + + return fileResult; + } + + protected createLuaLibModulesInfo(): { result: LuaLibModulesInfo; diagnostics: ts.Diagnostic[] } { + const result: Partial = {}; + const diagnostics: ts.Diagnostic[] = []; + for (const feature of Object.values(tstl.LuaLibFeature)) { + const exports = this.featureExports.get(feature); + if (!exports) { + diagnostics.push(lualibDiagnostic(`Missing file for lualib feature: ${feature}`)); + continue; + } + const dependencies = this.featureDependencies.get(feature); + result[feature] = { + exports: Array.from(exports), + dependencies: dependencies ? Array.from(dependencies) : undefined, + }; + } + return { result: result as LuaLibModulesInfo, diagnostics }; + } +} + +class LuaLibPrinter extends tstl.LuaPrinter { + // Strip all exports during print + public printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode { + if ( + tstl.isIdentifier(expression.table) && + expression.table.text === "____exports" && + tstl.isStringLiteral(expression.index) + ) { + return super.printExpression(tstl.createIdentifier(expression.index.value)); + } + return super.printTableIndexExpression(expression); + } +} + +const pluginInstance = new LuaLibPlugin(); +// eslint-disable-next-line import/no-default-export +export default pluginInstance; diff --git a/src/lualib-build/util.ts b/src/lualib-build/util.ts new file mode 100644 index 000000000..c8ea1e3a8 --- /dev/null +++ b/src/lualib-build/util.ts @@ -0,0 +1,47 @@ +import * as tstl from ".."; + +export function isExportTableDeclaration(node: tstl.Node): node is tstl.VariableDeclarationStatement & { left: [] } { + return tstl.isVariableDeclarationStatement(node) && isExportTable(node.left[0]); +} + +export function isExportTable(node: tstl.Node): node is tstl.Identifier { + return tstl.isIdentifier(node) && node.text === "____exports"; +} + +export type ExportTableIndex = tstl.TableIndexExpression & { index: tstl.StringLiteral }; +export function isExportTableIndex(node: tstl.Node): node is ExportTableIndex { + return tstl.isTableIndexExpression(node) && isExportTable(node.table) && tstl.isStringLiteral(node.index); +} + +export function isExportAlias( + node: tstl.Node +): node is tstl.VariableDeclarationStatement & { right: [ExportTableIndex] } { + return tstl.isVariableDeclarationStatement(node) && node.right !== undefined && isExportTableIndex(node.right[0]); +} + +export type ExportAssignment = tstl.AssignmentStatement & { left: [ExportTableIndex] }; +export function isExportAssignment(node: tstl.Node): node is ExportAssignment { + return tstl.isAssignmentStatement(node) && isExportTableIndex(node.left[0]); +} + +export function isRequire(node: tstl.Node) { + return ( + tstl.isVariableDeclarationStatement(node) && + node.right && + tstl.isCallExpression(node.right[0]) && + tstl.isIdentifier(node.right[0].expression) && + node.right[0].expression.text === "require" + ); +} + +export function isImport(node: tstl.Node, importNames: Set) { + return tstl.isVariableDeclarationStatement(node) && importNames.has(node.left[0].text); +} + +export function isExportsReturn(node: tstl.Node) { + return ( + tstl.isReturnStatement(node) && + tstl.isIdentifier(node.expressions[0]) && + node.expressions[0].text === "____exports" + ); +} 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..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/ -enum __TS__PromiseState { +export const enum 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,12 +23,12 @@ 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 { - public state = __TS__PromiseState.Pending; +export class __TS__Promise implements Promise { + public state = PromiseState.Pending; public value?: T; public rejectionReason?: any; @@ -42,7 +42,7 @@ 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 @@ 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; } @@ -70,10 +70,10 @@ 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; + const isFulfilled = this.state === PromiseState.Fulfilled; + const isRejected = this.state === PromiseState.Rejected; if (onFulfilled) { const internalCallback = this.createPromiseResolvingCallback(onFulfilled, resolve, reject); @@ -110,16 +110,18 @@ 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) { 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(); } @@ -137,8 +139,8 @@ 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) { @@ -152,8 +154,8 @@ 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) { @@ -179,18 +181,19 @@ 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 (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 e0d940977..c9263b645 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, 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>(); @@ -9,10 +11,10 @@ function __TS__PromiseAll(this: void, iterable: Iterable>) 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 its value to our results array results[i] = item.value; - } else if (item.state === __TS__PromiseState.Rejected) { + } else if (item.state === PromiseState.Rejected) { // If value is a rejected promise, return a rejected promise with the rejection reason return Promise.reject(item.rejectionReason); } else { diff --git a/src/lualib/PromiseAllSettled.ts b/src/lualib/PromiseAllSettled.ts index 165e226f4..4bcd13123 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, 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>>> { @@ -12,10 +14,10 @@ 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 eb70bf12f..17c77f273 100644 --- a/src/lualib/PromiseAny.ts +++ b/src/lualib/PromiseAny.ts @@ -1,15 +1,17 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any +import { __TS__Promise, 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> = []; 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, return a new resolved promise with its value return Promise.resolve(item.value); - } else if (item.state === __TS__PromiseState.Rejected) { + } else if (item.state === PromiseState.Rejected) { // If value is a rejected promise, add its value to our list of rejections rejections.push(item.rejectionReason); } else { diff --git a/src/lualib/PromiseRace.ts b/src/lualib/PromiseRace.ts index 3b3a1fb7b..238aaf33b 100644 --- a/src/lualib/PromiseRace.ts +++ b/src/lualib/PromiseRace.ts @@ -1,14 +1,16 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race +import { 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) { if (item instanceof __TS__Promise) { - if (item.state === __TS__PromiseState.Fulfilled) { + if (item.state === PromiseState.Fulfilled) { // If value is a fulfilled promise, return a resolved promise with its value return Promise.resolve(item.value); - } else if (item.state === __TS__PromiseState.Rejected) { + } else if (item.state === PromiseState.Rejected) { // If value is a rejected promise, return rejected promise with its value return Promise.reject(item.rejectionReason); } else { 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..8acde4cca 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -11,6 +11,7 @@ }, "tstl": { "luaLibImport": "none", - "noHeader": true + "noHeader": true, + "luaPlugins": [{ "name": "../../dist/lualib-build/plugin.js" }] } } diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index b56486425..59c0cf721 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"; @@ -33,6 +32,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 +52,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( @@ -66,7 +65,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) { @@ -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 46c3a8904..6a73bfeb6 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 { transformBuiltinIdentifierExpression, checkForLuaLibType } from "../builtins"; +import { isPromiseClass, createPromiseIdentifier } from "../builtins/promise"; import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context"; import { AnnotationKind, isForRangeType } from "../utils/annotations"; import { @@ -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 { isStandardLibraryType } from "../utils/typescript"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { if (isOptionalContinuation(identifier)) { @@ -59,6 +60,10 @@ export function transformIdentifier(context: TransformationContext, identifier: importLuaLibFeature(context, LuaLibFeature.Promise); return createPromiseIdentifier(identifier); } + 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/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index d8daba8c9..a9a53f4e4 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -72,12 +72,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 +140,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/transpilation/bundle.ts b/src/transpilation/bundle.ts index 45fe6d409..0c039ad4f 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -104,7 +104,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const footers: string[] = []; if (options.sourceMapTraceback) { // Generates SourceMapTraceback for the entire file - footers.push('require("lualib_bundle")\n'); + footers.push('local __TS__SourceMapTraceBack = require("lualib_bundle").__TS__SourceMapTraceBack\n'); footers.push(`${sourceMapTracebackBundlePlaceholder}\n`); } diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index 0311efbbd..8b4070591 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -1,8 +1,9 @@ import * as ts from "typescript"; +import { EmitHost } from ".."; import { CompilerOptions } from "../CompilerOptions"; import { Printer } from "../LuaPrinter"; import { Visitors } from "../transformation/context"; -import { getConfigDirectory, resolvePlugin } from "./utils"; +import { getConfigDirectory, ProcessedFile, resolvePlugin } from "./utils"; export interface Plugin { /** @@ -18,6 +19,21 @@ export interface Plugin { * At most one custom printer can be provided across all plugins. */ printer?: Printer; + + /** + * This function is called before transpilation of the TypeScript program starts. + */ + beforeTransform?: (program: ts.Program, options: CompilerOptions, emitHost: EmitHost) => ts.Diagnostic[] | void; + + /** + * This function is called after TypeScriptToLua has translated the input program to Lua. + */ + afterPrint?: ( + program: ts.Program, + options: CompilerOptions, + emitHost: EmitHost, + result: ProcessedFile[] + ) => ts.Diagnostic[] | void; } export function getPlugins(program: ts.Program, diagnostics: ts.Diagnostic[], customPlugins: Plugin[]): Plugin[] { diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index fc645147f..bf226992f 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -66,6 +66,13 @@ export function getProgramTranspileResult( console.log(`Successfully loaded ${plugins.length} plugins`); } + for (const plugin of plugins) { + if (plugin.beforeTransform) { + const pluginDiagnostics = plugin.beforeTransform(program, options, emitHost) ?? []; + diagnostics.push(...pluginDiagnostics); + } + } + const visitorMap = createVisitorMap(plugins.map(p => p.visitors).filter(isNonNull)); const printer = createPrinter(plugins.map(p => p.printer).filter(isNonNull)); const processSourceFile = (sourceFile: ts.SourceFile) => { @@ -129,5 +136,12 @@ export function getProgramTranspileResult( transpiledFiles = []; } + for (const plugin of plugins) { + if (plugin.afterPrint) { + const pluginDiagnostics = plugin.afterPrint(program, options, emitHost, transpiledFiles) ?? []; + diagnostics.push(...pluginDiagnostics); + } + } + return { diagnostics, transpiledFiles }; } diff --git a/test/cli/parse.spec.ts b/test/cli/parse.spec.ts index f40cf960c..921af9c45 100644 --- a/test/cli/parse.spec.ts +++ b/test/cli/parse.spec.ts @@ -111,7 +111,6 @@ describe("command line", () => { ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], - ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], ["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }], @@ -225,7 +224,6 @@ describe("tsconfig", () => { ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], - ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], ["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }], 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/transpile/__snapshots__/directories.spec.ts.snap b/test/transpile/__snapshots__/directories.spec.ts.snap index eaf179a6c..a76619460 100644 --- a/test/transpile/__snapshots__/directories.spec.ts.snap +++ b/test/transpile/__snapshots__/directories.spec.ts.snap @@ -3,7 +3,6 @@ exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 1`] = ` Array [ "directories/basic/src/lib/file.lua", - "directories/basic/src/lualib_bundle.lua", "directories/basic/src/main.lua", ] `; @@ -11,7 +10,6 @@ Array [ exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 2`] = ` Array [ "directories/basic/out/lib/file.lua", - "directories/basic/out/lualib_bundle.lua", "directories/basic/out/main.lua", ] `; @@ -19,7 +17,6 @@ Array [ exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 3`] = ` Array [ "directories/basic/src/lib/file.lua", - "directories/basic/src/lualib_bundle.lua", "directories/basic/src/main.lua", ] `; @@ -27,7 +24,6 @@ Array [ exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 4`] = ` Array [ "directories/basic/out/lib/file.lua", - "directories/basic/out/lualib_bundle.lua", "directories/basic/out/main.lua", ] `; diff --git a/test/transpile/directories.spec.ts b/test/transpile/directories.spec.ts index 1c0bba994..9572c1e0d 100644 --- a/test/transpile/directories.spec.ts +++ b/test/transpile/directories.spec.ts @@ -19,7 +19,7 @@ test.each([ const config = { compilerOptions: { ...compilerOptions, types: [], skipLibCheck: true }, - tstl: { luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.Always }, + tstl: { luaTarget: tstl.LuaTarget.LuaJIT }, }; const { fileNames, options } = tstl.updateParsedConfigFile( diff --git a/test/transpile/lualib.spec.ts b/test/transpile/lualib.spec.ts index 0af391c46..b35ab996b 100644 --- a/test/transpile/lualib.spec.ts +++ b/test/transpile/lualib.spec.ts @@ -1,26 +1,31 @@ import * as ts from "typescript"; import { LuaLibFeature } from "../../src"; -import { loadLuaLibFeatures } from "../../src/LuaLib"; +import { readLuaLibFeature } from "../../src/LuaLib"; +import * as util from "../util"; -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 = []; + const exportsOccurrences = lualibCode.match(/____exports/g); + expect(exportsOccurrences).toBeNull(); +}); - if (luaLibReferences !== null) { - for (const reference of luaLibReferences) { - if ( - !lualibCode.includes(`function ${reference}`) && - !lualibCode.includes(`${reference.substring(0, reference.length - 1)} =`) - ) { - missingReferences.push(reference); +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); } } - } - - expect(missingReferences).toHaveLength(0); + ` + .withLanguageExtensions() + .expectNoExecutionError(); }); diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index b60c83e91..83836f6b7 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", ` - require("lualib_bundle") + local ____lualib = require("lualib_bundle") + local __TS__ArrayPush = ____lualib.__TS__ArrayPush local result = {} __TS__ArrayPush(result, "foo") diff --git a/test/transpile/plugins/afterPrint.ts b/test/transpile/plugins/afterPrint.ts new file mode 100644 index 000000000..5d0d3e78d --- /dev/null +++ b/test/transpile/plugins/afterPrint.ts @@ -0,0 +1,22 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + afterPrint( + program: ts.Program, + options: tstl.CompilerOptions, + emitHost: tstl.EmitHost, + result: tstl.ProcessedFile[] + ) { + void program; + void options; + void emitHost; + + for (const file of result) { + file.code = "-- Commented added by afterPrint plugin\n" + file.code; + } + }, +}; + +// eslint-disable-next-line import/no-default-export +export default plugin; diff --git a/test/transpile/plugins/beforeTransform.ts b/test/transpile/plugins/beforeTransform.ts new file mode 100644 index 000000000..947358fa2 --- /dev/null +++ b/test/transpile/plugins/beforeTransform.ts @@ -0,0 +1,15 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + beforeTransform(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost) { + void program; + void emitHost; + + // Modify settings + options.outDir = "plugin/beforeTransform/outdir"; + }, +}; + +// eslint-disable-next-line import/no-default-export +export default plugin; diff --git a/test/transpile/plugins/plugins.spec.ts b/test/transpile/plugins/plugins.spec.ts index 457d7d2d9..614e2f1ca 100644 --- a/test/transpile/plugins/plugins.spec.ts +++ b/test/transpile/plugins/plugins.spec.ts @@ -78,8 +78,40 @@ test.each(["namespace", "module"])("%s with TS transformer plugin", moduleOrName return false; } } - ` + ` ) .setOptions({ plugins: [{ transform: path.join(__dirname, "transformer-plugin.ts") }] }) .expectNoExecutionError(); }); + +test("beforeTransform plugin", () => { + const { transpiledFiles } = util.testModule` + console.log("Hello, World!"); + ` + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "beforeTransform.ts") }] }) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(1); + // Expect emitted to output path set by the plugin + expect(transpiledFiles[0].outPath).toContain(path.join("plugin", "beforeTransform", "outdir")); +}); + +test("afterPrint plugin", () => { + const { transpiledFiles } = util.testModule` + console.log("Hello, World!"); + ` + .addExtraFile( + "extrafile.ts", + ` + console.log("Hello, Mars!"); + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "afterPrint.ts") }] }) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(2); + for (const f of transpiledFiles) { + // Expect plugin inserted extra lua at start of file + expect(f.lua).toContain("-- Commented added by afterPrint plugin"); + } +}); 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/builtins/loading.spec.ts b/test/unit/builtins/loading.spec.ts index fa213d03f..a5da97772 100644 --- a/test/unit/builtins/loading.spec.ts +++ b/test/unit/builtins/loading.spec.ts @@ -16,13 +16,6 @@ describe("luaLibImport", () => { .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain('require("lualib_bundle")')) .expectToMatchJsResult(); }); - - test("always", () => { - util.testModule`` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always }) - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain('require("lualib_bundle")')) - .expectToEqual(undefined); - }); }); test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( 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(); +}); 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" }, ],