From 4f715e02964a63dce2ea8d345577133b7ee95db5 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 25 Feb 2020 23:11:43 +0000 Subject: [PATCH 01/23] Import documentation from the wiki --- docs/api/overview.md | 156 +++++ docs/api/printer.md | 81 +++ docs/api/transformer.md | 193 ++++++ docs/compiler-annotations.md | 428 ++++++++++++ docs/contributors/design-goals.md | 25 + .../differences-between-lua-and-javascript.md | 58 ++ docs/functions-and-the-self-parameter.md | 194 ++++++ docs/getting-started.md | 79 +++ docs/limitations.md | 71 ++ docs/supported-lua-versions.md | 5 + docs/writing-declarations.md | 639 ++++++++++++++++++ docusaurus.config.js | 5 + sidebars.json | 21 + src/pages/index.tsx | 6 + 14 files changed, 1961 insertions(+) create mode 100644 docs/api/overview.md create mode 100644 docs/api/printer.md create mode 100644 docs/api/transformer.md create mode 100644 docs/compiler-annotations.md create mode 100644 docs/contributors/design-goals.md create mode 100644 docs/differences-between-lua-and-javascript.md create mode 100644 docs/functions-and-the-self-parameter.md create mode 100644 docs/getting-started.md create mode 100644 docs/limitations.md create mode 100644 docs/supported-lua-versions.md create mode 100644 docs/writing-declarations.md create mode 100644 sidebars.json diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 00000000..de5f4df8 --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,156 @@ +--- +title: Overview +--- + +TypeScriptToLua provides a high-level and a low-level API. The high-level API can be used to invoke basic transpiler operations. The low-level API can be used to extend and override default transpiler behavior, to customize it to your specific environment. + +## High-level API + +The high level API allows you to simply invoke several common transpiler operations from code. + +### TranspileString + +Transpile a string containing TypeScript source code to Lua. + +**Arguments:** + +- Source: string - The TypeScript source code to transpile. +- _[Optional]_ Options: tstl.CompilerOptions - CompilerOptions to use. + +**Example:** + +```ts +import * as tstl from "typescript-to-lua"; + +const result = tstl.transpileString(`const foo = "bar";`, { luaTarget: tstl.LuaTarget.Lua53 }); +console.log(result.diagnostics); +console.log(result.file); +``` + +### TranspileFiles + +Transpile a collection of TypeScript files to Lua. + +**Arguments:** + +- FileNames: string[] - An array of file paths to the TypeScript files to be transpiled. +- _[Optional]_ Options: tstl.CompilerOptions - CompilerOptions to use. + +**Example:** + +```ts +import * as tstl from "typescript-to-lua"; + +const result = tstl.transpileFiles(["file1.ts", "file2.ts"], { luaTarget: tstl.LuaTarget.Lua53 }); +console.log(result.diagnostics); +console.log(result.emitResult); +``` + +### TranspileProject + +Transpile a TypeScript project to Lua. + +**Arguments:** + +- tsConfigPath: string - The file path to a TypeScript project's `tsconfig.json` file. +- _[Optional]_ extendedOptions: tstl.CompilerOptions - The tsConfig already contains options, this extends those options. + +**Example:** + +```ts +import * as tstl from "typescript-to-lua"; + +const result = tstl.transpileProject("tsconfig.json", { luaTarget: tstl.LuaTarget.Lua53 }); +console.log(result.diagnostics); +console.log(result.emitResult); +``` + +### TranspileVirtualProject + +Transpile a virtual project to Lua. A virtual project is a record (like an object literal for example) where keys are file names, and values are the contents of these files. This can be used to transpile a collection of files without having these files physically on disk. + +**Arguments:** + +- Files: Record - A record of fileName keys and fileContent values. +- _[Optional]_ Options: tstl.CompilerOptions - CompilerOptions to use. + +**Example:** + +```ts +import * as tstl from "typescript-to-lua"; + +const result = tstl.transpileVirtualProject( + { + "file1.ts": `const foo = "bar";`, + "file2.ts": `const bar = "baz";`, + }, + { luaTarget: tstl.LuaTarget.Lua53 }, +); +console.log(result.diagnostics); +console.log(result.transpiledFiles); +``` + +## Low-level API + +The low-level TypeScriptToLua API allows for extending or modifying of the default tstl transpilation process. For this to make sense it is important to know the process can broadly be split into **two phases: transforming and printing**. The first step of each transpilation is to **transform** the TypeScript AST of each file to a Lua AST. The next step is to **print** the resulting Lua AST to a string. TypeScriptToLua therefore implements a **LuaTransformer** and **LuaPrinter**. These two classes can be modified using the low-level API. + +### Transpile + +The low-level API consists of only one function: `transpile`. It takes a TypeScript program, optional source files, and optional custom transformer and printer arguments, allowing you to override their default behavior first. + +More information on extending the transformer and printer can be found here: + +- [Custom LuaTransformer API](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaTransformer-API) +- [Custom LuaPrinter API](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaPrinter-API) + +**Arguments:** + +- program: ts.Program - The TypeScript program to transpile (note: unlike the high-level API, compilerOptions is part of the program and cannot be supplied separately). +- _[Optional]_ sourceFiles: ts.SourceFile[] - A collection of sourcefiles to transpile, `program.getSourceFiles()` by default. +- _[Optional]_ customTransformers: ts.CustomTransformers - Custom TypeScript transformers to apply before transpiling. +- _[Optional]_ transformer: tstl.LuaTransformer - If provided, this transformer is used instead of the default tstl transformer. +- _[Optional]_ printer: tstl.LuaPrinter - If provided, this printer is used instead of the default tstl printer. +- _[Optional]_ emitHost: tstl.EmitHost - Provides the methods for reading/writing files, useful in cases where you need something other than regular reading from disk. Defaults to `ts.sys`. + +**Example:** + +This example shows using the low-level API to override how array literals are transpiled. By default, array literals are transformed as follows: `(TS)[1, 2, 3] -> (Lua){1, 2, 3}`. This example extends and overrides the default transformer to instead transform like this: `(TS)[1, 2, 3] -> (Lua){1, 2, 3, n=3}`. + +```ts +const options: tstl.CompilerOptions = { luaTarget: tstl.LuaTarget.Lua53 }; +const program = ts.createProgram({ rootNames: ["file1.ts", "file2.ts"], options }); + +class CustomTransformer extends tstl.LuaTransformer { + public transformArrayLiteral(expression: ts.ArrayLiteralExpression): tstl.ExpressionVisitResult { + // Call the original transformArrayLiteral first, to get the default result. + // You could also skip this and create your own table expression with tstl.createTableExpression() + const result = super.transformArrayLiteral(expression) as tstl.TableExpression; + + // Create the 'n = ' node + const nIdentifier = tstl.createIdentifier("n"); + const nValue = tstl.createNumericLiteral(expression.elements.length); + const tableField = tstl.createTableFieldExpression(nValue, nIdentifier); + + // Add the extra table field we created to the default transformation result + if (result.fields === undefined) { + result.fields = []; + } + result.fields.push(tableField); + + return result; + } +} + +const transformer = new CustomTransformer(program); +const printer = new tstl.LuaPrinter(options); + +const result = tstl.transpile({ + program, + transformer, + printer, +}); +console.log(result.diagnostics); +console.log(result.transpiledFiles); +// Emit result +console.log(tstl.emitTranspiledFiles(options, result5.transpiledFiles)); +``` diff --git a/docs/api/printer.md b/docs/api/printer.md new file mode 100644 index 00000000..76bfce67 --- /dev/null +++ b/docs/api/printer.md @@ -0,0 +1,81 @@ +--- +title: LuaPrinter +--- + +The [LuaPrinter](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/src/LuaPrinter.ts) class takes Lua AST and prints it to a string (with source map). Like the LuaTransformer, the printer implements the visitor pattern. All methods visit nodes in the AST to print them to a `SourceNode`, this will automatically produce correct mappings in the resulting source map. + +## Visitor Pattern + +Like the LuaTransformer, the LuaPrinter class also implements a visitor pattern. For more explanation see the [visitor pattern explanation on the LuaTransformer page](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaTransformer-API#visitor-pattern) + +## API Reference + +This is a list of all public overridable methods in the default TypeScriptToLua printer: + +```ts +class LuaPrinter { + public printStatement(statement: tstl.Statement): SourceNode; + + public printDoStatement(statement: tstl.DoStatement): SourceNode; + + public printVariableDeclarationStatement(statement: tstl.VariableDeclarationStatement): SourceNode; + + public printVariableAssignmentStatement(statement: tstl.AssignmentStatement): SourceNode; + + public printIfStatement(statement: tstl.IfStatement): SourceNode; + + public printWhileStatement(statement: tstl.WhileStatement): SourceNode; + + public printRepeatStatement(statement: tstl.RepeatStatement): SourceNode; + + public printForStatement(statement: tstl.ForStatement): SourceNode; + + public printForInStatement(statement: tstl.ForInStatement): SourceNode; + + public printGotoStatement(statement: tstl.GotoStatement): SourceNode; + + public printLabelStatement(statement: tstl.LabelStatement): SourceNode; + + public printReturnStatement(statement: tstl.ReturnStatement): SourceNode; + + public printBreakStatement(statement: tstl.BreakStatement): SourceNode; + + public printExpressionStatement(statement: tstl.ExpressionStatement): SourceNode; + + public printExpression(expression: tstl.Expression): SourceNode; + + public printStringLiteral(expression: tstl.StringLiteral): SourceNode; + + public printNumericLiteral(expression: tstl.NumericLiteral): SourceNode; + + public printNilLiteral(expression: tstl.NilLiteral): SourceNode; + + public printDotsLiteral(expression: tstl.DotsLiteral): SourceNode; + + public printBooleanLiteral(expression: tstl.BooleanLiteral): SourceNode; + + public printFunctionExpression(expression: tstl.FunctionExpression): SourceNode; + + public printFunctionDefinition(statement: tstl.FunctionDefinition): SourceNode; + + public printTableFieldExpression(expression: tstl.TableFieldExpression): SourceNode; + + public printTableExpression(expression: tstl.TableExpression): SourceNode; + + public printUnaryExpression(expression: tstl.UnaryExpression): SourceNode; + + public printBinaryExpression(expression: tstl.BinaryExpression): SourceNode; + + public printParenthesizedExpression(expression: tstl.ParenthesizedExpression): SourceNode; + + public printCallExpression(expression: tstl.CallExpression): SourceNode; + + public printMethodCallExpression(expression: tstl.MethodCallExpression): SourceNode; + + public printIdentifier(expression: tstl.Identifier): SourceNode; + + public printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode; + + public printOperator(kind: tstl.Operator): SourceNode; +} +``` diff --git a/docs/api/transformer.md b/docs/api/transformer.md new file mode 100644 index 00000000..ea054ff0 --- /dev/null +++ b/docs/api/transformer.md @@ -0,0 +1,193 @@ +--- +title: LuaTransformer +--- + +If you need some Lua transformations for a very specific environment, it is possible to implement these using custom transformers. Writing a custom transformer involves extending the [default TypeScriptToLua LuaTransformer class](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/src/LuaTransformer.ts) and overriding its functionality. + +### Visitor Pattern + +The LuaTransformer class implements the visitor pattern. Therefore to override specific transformation behavior for specific TypeScript statements, it is only necessary to override that specific statement's transform method. + +### Example + +Assume we want to add to add a `array.n` property to all array table literals, we need to override: + +```ts +transformArrayLiteral(expression: ts.ArrayLiteralExpression): ExpressionVisitResult +``` + +Since the rest of the functionality should be the same, we can simply copy paste the default implementation and add our extra parameter. + +The resulting transformer would look something like: + +```ts +class CustomTransformer extends tstl.LuaTransformer { + public transformArrayLiteral(node: ts.ArrayLiteralExpression): ExpressionVisitResult { + const values: tstl.TableFieldExpression[] = []; + + node.elements.forEach(e => { + const element = this.transformExpression(e); // We can just call the default transformations + values.push(tstl.createTableFieldExpression(element, undefined, e)); + }); + + // Custom behavior: Add n= to lua array table. + const nIdentifier = tstl.createStringLiteral("n"); + const nValue = tstl.createNumericLiteral(node.elements.length); + values.push(tstl.createTableFieldExpression(nValue, nIdentifier)); + // End of custom behavior + + return tstl.createTableExpression(values, node); + } +} +``` + +## API Reference + +This is a list of all public overridable methods in the default TypeScriptToLua tranformer: + +```ts +class LuaTransformer { + public transformSourceFile(sourceFile: ts.SourceFile): [tstl.Block, Set]; + + public transformStatement(statement: ts.Statement): StatementVisitResult; + + public transformBlock(block: ts.Block): tstl.Block; + + public transformExportDeclaration(declaration: ts.ExportDeclaration): StatementVisitResult; + + public transformImportDeclaration(declaration: ts.ImportDeclaration): StatementVisitResult; + + public transformClassDeclaration( + declaration: ts.ClassLikeDeclaration, + nameOverride?: tstl.Identifier, + ): StatementVisitResult; + + public transformGetAccessorDeclaration( + declaration: ts.GetAccessorDeclaration, + className: tstl.Identifier, + ): StatementVisitResult; + + public transformSetAccessorDeclaration( + declaration: ts.GetAccessorDeclaration, + className: tstl.Identifier, + ): StatementVisitResult; + + public transformMethodDeclaration( + declaration: ts.MethodDeclaration, + className: tstl.Identifier, + noPrototype: boolean, + ): StatementVisitResult; + + public transformBindingPattern( + pattern: ts.BindingPattern, + table: tstl.Identifier, + propertyStack: ts.PropertyName, + ): StatementVisitResult; + + public transformModuleDeclaration(declaration: ts.ModuleDeclaration): StatementVisitResult; + + public transformEnumDeclaration(declaration: ts.EnumDeclaration): StatementVisitResult; + + public transformFunctionDeclaration(declaration: ts.FunctionDeclaration): StatementVisitResult; + + public transformTypeAliasDeclaration(declaration: ts.TypeAliasDeclaration): StatementVisitResult; + + public transformInterfaceDeclaration(declaration: ts.InterfaceDeclaration): StatementVisitResult; + + public transformVariableDeclaration(declaration: ts.VariableDeclaration): StatementVisitResult; + + public transformVariableStatement(statement: ts.VariableStatement): StatementVisitResult; + + public transformExpressionStatement(statement: ts.ExpressionStatement | ts.Expression): StatementVisitResult; + + public transformReturnStatement(statement: ts.ReturnStatement): StatementVisitResult; + + public transformIfStatement(statement: ts.IfStatement): StatementVisitResult; + + public transformWhileStatement(statement: ts.WhileStatement): StatementVisitResult; + + public transformDoStatement(statement: ts.DoStatement): StatementVisitResult; + + public transformForStatement(statement: ts.ForStatement): StatementVisitResult; + + public transformForOfStatement(statement: ts.ForOfStatement): StatementVisitResult; + + public transformForInStatement(statement: ts.ForInStatement): StatementVisitResult; + + public transformSwitchStatement(statement: ts.SwitchStatement): StatementVisitResult; + + public transformBreakStatement(statement: ts.BreakStatement): StatementVisitResult; + + public transformTryStatement(statement: ts.TryStatement): StatementVisitResult; + + public transformThrowStatement(statement: ts.ThrowStatement): StatementVisitResult; + + public transformContinueStatement(statement: ts.ContinueStatement): StatementVisitResult; + + public transformEmptyStatement(statement: ts.EmptyStatement): StatementVisitResult; + + // Expressions + + public transformExpression(expression: ts.Expression): ExpressionVisitResult; + + public transformBinaryExpression(expression: ts.BinaryExpression): ExpressionVisitResult; + + public transformBinaryOperator(operator: ts.BinaryOperator, node: ts.Node): tstl.BinaryOperator; + + public transformClassExpression(expression: ts.ClassExpression): ExpressionVisitResult; + + public transformConditionalExpression(expression: ts.ConditionalExpression): ExpressionVisitResult; + + public transformPostfixUnaryExpression(expression: ts.PostfixUnaryExpression): ExpressionVisitResult; + + public transformPrefixUnaryExpression(expression: ts.PrefixUnaryExpression): ExpressionVisitResult; + + public transformArrayLiteral(expression: ts.ArrayLiteralExpression): ExpressionVisitResult; + + public transformObjectLiteral(expression: ts.ObjectLiteralExpression): ExpressionVisitResult; + + public transformDeleteExpression(expression: ts.DeleteExpression): ExpressionVisitResult; + + public transformFunctionExpresssion(expression: ts.FunctionExpression): ExpressionVisitResult; + + public transformNewExpression(expression: ts.NewExpression): ExpressionVisitResult; + + public transformParenthesizedExpression(expression: ts.ParenthesizedExpression): ExpressionVisitResult; + + public transformSuperKeyword(expression: ts.SuperExpression): ExpressionVisitResult; + + public transformCallExpression(expression: ts.CallExpression): ExpressionVisitResult; + + public transformPropertyAccessExpression(expression: ts.PropertyAccessExpression): ExpressionVisitResult; + + public transformElementAccessExpression(expression: ts.ElementAccessExpression): ExpressionVisitResult; + + public transformArrayBindingElement(expression: ts.ArrayBindingelement): ExpressionVisitResult; + + public transformAssertionExpression(expression: ts.AssertionExpression): ExpressionVisitResult; + + public transformTypeOfExpression(expression: ts.TypeOfExpression): ExpressionVisitResult; + + public transformSpreadElement(expression: ts.SpreadElement): ExpressionVisitResult; + + public transformStringLIteral(literal: ts.StringLiteralLike): ExpressionVisitResult; + + public transformNumericLiteral(literal: ts.NumericLiteral): ExpressionVisitResult; + + public transformTrueKeyword(keyword: ts.BooleanLiteral): ExpressionVisitResult; + + public transformFalseKeyword(keyword: ts.BooleanLiteral): ExpressionVisitResult; + + public transformNullOrUndefinedKeyword(keyword: ts.Node): ExpressionVisitResult; + + public transformThisKeyword(keyword: ts.ThisExpression): ExpressionVisitResult; + + public transformTemplateExpression(expression: ts.TemplateExpression): ExpressionVisitResult; + + public transformPropertyName(expression: ts.PropertyName): ExpressionVisitResult; + + public transformIdentifier(expression: ts.Identifier): tstl.Identifier; + + public transformYield(expression: ts.YieldExpression): ExpressionVisitResult; +} +``` diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md new file mode 100644 index 00000000..678bcd02 --- /dev/null +++ b/docs/compiler-annotations.md @@ -0,0 +1,428 @@ +--- +title: Compiler Annotations +--- + +To improve translation and compatibility to different Lua interfaces, the TypscriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax. + +#### Table of Contents + +- [@compileMembersOnly](#compilemembersonly) +- [@customConstructor](#customconstructor) +- [@extension](#extension) +- [@forRange](#forRange) +- [@luaIterator](#luaIterator) +- [@luaTable](#luaTable) +- [@metaExtension](#metaextension) +- [@noResolution](#noResolution) +- [@noSelf](#noself) +- [@noSelfInFile](#noselfinfile) +- [@phantom](#phantom) +- [@pureAbstract](#pureabstract) +- [@tupleReturn](#tuplereturn) +- [@vararg](#vararg) + +## @compileMembersOnly + +**Target elements:** `(declare) enum` + +This decorator removes an enumeration's name after compilation and only leaves its members. Primarily used for APIs with implicit enumerations. + +**Example** + +```typescript +declare enum myEnum { + myEnum_memberA, Translates to + myEnum_memberB, ============> +} +const myvar = myEnum.myEnum_memberA; local myvar = myEnum.myEnum_memberA +``` + +```typescript +/** @compileMembersOnly */ +declare enum myEnum { + myEnum_memberA, Translates to + myEnum_memberB, ============> +} +const myvar = myEnum.myEnum_memberA; local myvar = myEnum_memberA +``` + +**Example 2** + +```typescript +enum myEnum { myEnum = {} + myEnum_memberA, Translates to myEnum.myEnum_memberA = 0 + myEnum_memberB, ============> myEnum.myEnum_memberB = 1 + myEnum_memberC = "c" myEnum.myEnum_memberC = "c" +} +const myvar = myEnum.myEnum_memberA; local myvar = myEnum.myEnum_memberA +``` + +```typescript +/** @compileMembersOnly */ +enum myEnum { + myEnum_memberA, Translates to myEnum_memberA = 0 + myEnum_memberB, ============> myEnum_memberB = 1 + myEnum_memberC = "c" myEnum_memberC = "c" +} +const myvar = myEnum.myEnum_memberA; local myvar = myEnum_memberA +``` + +## @customConstructor + +**Target elements:** `declare class` + +Changes the way new instances of this class are made. Takes exactly one argument that is the name of the alternative constructor function. + +**Example** + +```typescript +declare class MyClass { constructor(x: number); } Translates to +const inst = new MyClass(3); ============> local inst = MyClass.new(true, 3); +``` + +```typescript +/** @customConstructor MyConstructor */ +declare class MyClass { constructor(x: number); } Translates to +const inst = new MyClass(3); ============> local inst = MyConstructor(3); +``` + +## @extension + +**Target elements:** `class` + +The Extension decorator marks a class as an extension of an already existing class. This causes the class header to not be translated, preventing instantiation and the override of the existing class. + +**Example** + +```typescript +class myBaseClass{ myBaseClass = myBaseClass or {} + myFunction(): void {} Translates to myBaseClass.__index = myBaseClass +} ============> ... + function myBaseClass.myFunction(self) end +``` + +```typescript +/** @extension */ +class myBaseClass{ Translates to + myFunction(): void {} ============> function myBaseClass.myFunction(self) end +} +``` + +## @forRange + +**Target elements:** `declare function` + +Denotes a function declaration is a Lua numerical iterator. When used in a Typescript `for...of` loop, the resulting Lua will use a numerical for loop. + +The function should not be a real function and an error will be thrown if it is used in any other way. + +**Example** + +```typescript +/** @forRange */ +declare function forRange(start: number, limit: number, step?: number): number[]; + + Translates to +for (const i of forRange(1, 10)) {} ============> for i = 1, 10 do end +for (const i of forRange(10, 1, -1)) {} for i = 10, 1, -1 do end +``` + +## @luaIterator + +**Target elements:** `(declare) interface` + +Denotes a type is a Lua iterator. When an object of a type with this annotation is used in a for...of statement, it will transpile directly as a lua iterator in a for...in statement, instead of being treated as a Typescript iterable. Typically, this is used on an interface that extends `Iterable` or `Array` so that Typescript will allow it to be used in a for...of statement. + +**Example** + +```typescript +/** @luaIterator */ +interface MyIterable extends Iterable {} +declare function myIterator(): MyIterable; + Translates to +for (let s of myIterator()) {} ============> for s in myIterator() do end +``` + +This can also be combined with [@tupleReturn](#tuplereturn), if the iterator returns multiple values. + +**Example** + +```typescript +// Lua's built-in string.gmatch() iterator +declare namespace string { + /** @luaIterator @tupleReturn */ + export interface GmatchIterable extends Array {} + + export function gmatch(s: string, pattern: string): GmatchIterable; +} + + Translates to +for (const [a, b] of string.gmatch("foo", "(.)(.)")) {} ============> for a, b in string.gmatch("foo", "(.)(.)") do end +``` + +## @luaTable + +**Target elements:** `type` + +This annotation signals the transpiler to translate a class as a simple lua table for optimization purposes. + +```ts +/** @luaTable */ +declare class Table { + readonly length: number; + set(key: K, value: V | undefined): void; + get(key: K): V | undefined; +} + +const tbl = new Table(); // local tbl = {} + +const foo = {}; +tbl.set(foo, "bar"); // tbl[foo] = "bar" +print(tbl.get(foo)); // print(tbl[foo]) + +tbl.set(1, "baz"); // tbl[1] = "baz" +print(tbl.length); // print(#tbl) +``` + +## @metaExtension + +**Target elements:** `class` + +The Extension decorator marks a class as an extension of an already existing meta class/table. This causes the class header to not be translated, preventing instantiation and the override of the existing class. + +**Example** + +```typescript +class myBaseClass{ myBaseClass = myBaseClass or {} + myFunction(): void {} Translates to myBaseClass.__index = myBaseClass +} ============> ... + function myBaseClass.myFunction(self) end +``` + +```typescript +/** @metaExtension */ +class myMetaExtension extends myMetaClass { + myFunction(): void {} +} + +Translates to +============> + +local __meta__myMetaClass = debug.getregistry()["myMetaClass"] +__meta__myMetaClass.myFunction = function(self) +end; +``` + +## @noResolution + +**Target elements:** `module` + +Prevents tstl from trying to resolve the module path. When importing this module the path will be exactly the path in the import statement. + +**Example** + +```typescript +module MyModule { Translates to +} ============> ... +import module from "mymodule"; local module = require("src.mymodule"); +``` + +```typescript +/** @noResolution */ +module MyModule { Translates to +} ============> ... +import module from "mymodule"; local module = require("mymodule"); +``` + +## @noSelf + +**Target elements:** `declare class`, `(declare) interface` or `declare namespace` + +Indicates that functions inside a scope do not take in initial `self` argument when called, and thus will be called with a dot `.` instead of a colon `:`. It is the same as if each function was declared with an explicit `this: void` parameter. Functions that already have an explicit `this` parameter will not be affected. + +When applied to a class or interface, this only affects the type's declared methods (including static methods and fields with a function type). It will not affect other function declarations, such as nested functions inside a class' methods. + +**Example** + +```typescript +declare interface NormalInterface { + normalMethod(s: string): void; +} +declare const x: NormalInterface; + +/** @noSelf **/ +declare interface NoSelfInterface { + noSelfMethod(s: string): void; +} +declare const y: NoSelfInterface; + +x.normalMethod("foo"); Translates to x:normalMethod("foo") +y.noSelfMethod("bar"); ============> y.noSelfMethod("bar") +``` + +When applied to a namespace, all functions declared within the namespace will treated as if they do not have a `self` parameter. In this case, the effect is recursive, so functions in nested namespaces and types declared as parameters will also be affected. + +**Example** + +```typescript +declare namespace NormalNS { + export function normalFunc(s: string): string; +} + +/** @noSelf **/ +declare namespace NoSelfNS { + export function noSelfFunc(s: string): string; +} + +NormalNS.normalFunc("foo"); Translates to NormalNS:normalFunc("foo") +NoSelfNS.noSelfFunc("bar"); ============> NoSelfNS.noSelfFunc("bar") +``` + +For more information about how the `self` parameter is handled, see [Functions and the `self` Parameter](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Functions-and-the-%60self%60-Parameter) + +## @noSelfInFile + +**Target elements:** `(declare) file` + +Indicates that functions in a file do not take in initial `self` argument when called. + +This is annotation works the same as [@noSelf](#noself) being applied to a namespace, but affects the entire file. + +`@noSelfInFile` must be placed at the top of the file, before the first statement. + +## @phantom + +**Target elements:** `namespace` + +This decorator marks a namespace as a phantom namespace. This means all members of the namespace will be translated as if they were not in that namespace. Primarily used to prevent scoping issues. + +**Example** + +```typescript +namespace myNameSpace { Translates to myNameSpace = {} + function myFunction(): void {} ============> function myNameSpace.myFunction() end +} +``` + +```typescript +/** !phantom */ +namespace myNameSpace { Translates to + function myFunction(): void {} ============> function myFunction() end +} +``` + +## @pureAbstract + +**Target elements:** `declare class` + +This decorator marks a class declaration as purely abstract. The result is that any class extending the purely abstract class will not extend this class in the resulting Lua. + +**Example** + +```typescript +declare class myAbstractClass { myClass = myClass or myAbstractClass.new() +} Translates to myClass.__index = myClass +class myClass extends myAbstractClass { ============> myClass.__base = myAbstractClass +} function myClass.new(... +``` + +```typescript +/** @pureAbstract */ +declare class myAbstractClass { Translates to myClass = myClass or {} +} ============> myClass.__index = myClass +class myClass extends myAbstractClass { function myClass.new(... +} +``` + +## @tupleReturn + +**Target elements:** `(declare) function` + +This decorator indicates a function returns a lua tuple instead of a table. It influences both destructing assignments of calls of that function, as well as changing the format of returns inside the function body. + +**Example** + +```typescript +function myFunction(): [number, string] { function myFunction() + return [3, "4"]; Translates to return {3, "4"} +} ============> end +let [a,b] = myFunction(); local a,b = unpack(myFunction()) +``` + +```typescript +/** @tupleReturn */ +function myFunction(): [number, string] { function myFunction() + return [3, "4"]; Translates to return 3, "4" +} ============> end +let [a,b] = myFunction(); local a, b = myFunction() +``` + +If you wish to use this annotation on function with overloads, it must be applied to each signature that requires it. + +**Example** + +```typescript +/** @tupleReturn */ +declare function myFunction(s: string): [string, string]; +/** @tupleReturn */ +declare function myFunction(n: number): [number, number]; +``` + +Note that if any overloaded signature of a function implementation has the annotation, all array/tuple return values will unpacked in the transpiled output. + +## @vararg + +**Target elements:** `(declare) interface or type` + +Indicates that an array-like type represents a Lua vararg expression (`...`) and should be transpiled to that when used in a spread expression. This is useful for forwarding varargs instead of wrapping them in a table and unpacking them. + +**Example** + +```typescript +function varargWrapUnpack(...args: string[]) { Translates to + console.log(...args); ============> local args = ({...}) +} print(unpack(args)) + +/** @vararg */ +interface Vararg extends Array {} + +function varargForward(...args: Vararg) { Translates to + console.log(...args); ============> print(...) +} +``` + +This can be used to access the file-scope varargs as well. + +**Example** + +```typescript +declare const arg: Vararg; Translates to +console.log(...arg); ============> print(...) +const [x, y] = [...arg]; local x, y = ... +``` + +To also support tuple-typed rest parameters, you can define the type like this: + +**Example** + +```typescript +/** @vararg */ +type Vararg = A & { __luaVararg?: never }; + +function varargForward(...args: Vararg<[string, number]>) {} +``` + +**_Warning_** + +TypeScriptToLua does not check that the vararg expression is valid in the context it is used. If the array is used in a spread operation in an invalid context (such as a nested function), a Lua compiler error will occur. + +**Example** + +```typescript +function outerFunction(...args: Vararg) { + function innerFunction() { + console.log(...args); // cannot use '...' outside a vararg function + } + innerFunction(); +} +``` diff --git a/docs/contributors/design-goals.md b/docs/contributors/design-goals.md new file mode 100644 index 00000000..8769bdc9 --- /dev/null +++ b/docs/contributors/design-goals.md @@ -0,0 +1,25 @@ +--- +title: Project Design Goals +--- + +The design goals of this project can be summarized in the following sentence: + +> TypeScriptToLua aims to transpile valid TypeScript and declarations to working Lua for any Lua version and platform API. + +Ofcourse there are several nuances to be taken into account here, as will be discussed on the rest of this page. + +## Declarations First + +The main goal of this project is for it to be applicable to any Lua environment and API. This means the process of declaring platform APIs and existing Lua functionality are central in the design of the transpiler. As a result of this, TypeScriptToLua supports several [Compiler directives](https://github.com/Perryvw/TypescriptToLua/wiki/Compiler-Directives) that affect the resulting Lua to better match API declarations. + +## Functional equivalence Lua/JavaScript + +Code written in TypeScript can be transpiled into both JavaScript and Lua. It would therefore be reasonable to expect that two programs with the same source are functionally equivalent: this is **NOT** the case. + +We aim to keep the functionality between transpiled JavaScript and Lua equivalent as much as possible. However, since JavaScript and Lua are fundamentally differences, equivalence of functionality can not be guaranteed. See [Differences between Lua and JavaScript](https://github.com/Perryvw/TypescriptToLua/wiki/Differences-between-Lua-and-Javascript) for more details. + +We will try to stay functionally equivalent as much as possible, but not at all costs. If the workaround needed to support equivalent JavaScript functionality is too complex or flawed, we may accept slightly different functionality in Lua compared to JavaScript. A list of such limitations can be found here: [Limitations](https://github.com/Perryvw/TypescriptToLua/wiki/Limitations). + +## Optimization strategy + +Since this project aims to be usable in any Lua environment, it can not optimize in any direction. We also value readable (and understandable) Lua output. We obviously aim for the best performance, but balancing all of the previously mentioned concerns. diff --git a/docs/differences-between-lua-and-javascript.md b/docs/differences-between-lua-and-javascript.md new file mode 100644 index 00000000..77133db2 --- /dev/null +++ b/docs/differences-between-lua-and-javascript.md @@ -0,0 +1,58 @@ +--- +title: Differences between Lua and JS +--- + +TypeScript can be compiled to Lua using TypeScriptToLua. Since the input is still valid TypeScript it can also still be compiled to Javascript with the default TypeScript workflow. +This project aims for both compilation results to have the same behavior as much as possible, but not at all costs. Since TypeScript is based on JavaScript it also inherited some of the quirks in JavaScript that are not present in Lua. This is where behavior between Lua and JavaScript compilation targets diverge. TypeScriptToLua aims to keep identical behavior as long as **sane** TypeScript is used: if JavaScript-specific quirks are used behavior might differ. + +Below are some of the cases where resulting Lua intentionally behaves different from compiled JS. + +# True/False values + +JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. Therefore there is also no difference between `==` and `===` when compiling to Lua, all comparisons are strict (`===`). + +| TypeScript | _JavaScript behavior_ | _Lua behavior_ | +| ----------------- | --------------------- | -------------- | +| `false` | `false` | `false` | +| `undefined` | `false` | `nil`→ `false` | +| `null` | `false` | `nil`→ `false` | +| `NaN` | `false` | `nil`→ `false` | +| `""` | ⚠️`false` | ⚠️`true` | +| `0` | ⚠️`false` | ⚠️`true` | +| (Everything else) | `false` | `false` | + +# List Length + +List length is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between Javascript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`. + +##Exmples: + +**Safe (no difference):** + +```ts +const myList = [1, 2, 3]; +myList.push(4); +myList.pop(); +myList.splice(1, 1); +// myList.length == 2 +``` + +**Differences might occur:** + +```ts +const myList = [1, 2, 3]; +myList[1] = undefined; +// myList.length == 1 (3 in JavaScript) +``` + +```ts +const myList = [1, 2, 3]; +myList[4] = 5; +// myList.length == 3 (5 in JavaScript) +``` + +# Key Iteration Order + +Even though iterating over object keys with `for ... in` does not guarantee order in either JavaScript or Lua. Therefore, the iteration order in JavaScript is likely different from the order in Lua. + +**Note:** If a specific order is required, it is better to use ordered collections like lists instead. diff --git a/docs/functions-and-the-self-parameter.md b/docs/functions-and-the-self-parameter.md new file mode 100644 index 00000000..42e00b15 --- /dev/null +++ b/docs/functions-and-the-self-parameter.md @@ -0,0 +1,194 @@ +--- +title: Functions and the `self` Parameter +--- + +# Every Function Has a Context Parameter + +In JavaScript and TypeScript, almost all functions have access to an implicit `this` parameter. In order to maintain compatibility with this, all Lua functions are generated with an extra initial context parameter. + +**Example** + +```typescript +function myFunction(arg: unknown) {} +myFunction("foo"); +``` + +```lua +function myFunction(self, arg) +end +myFunction(nil, "foo") +``` + +The reason for this is that a method can be assigned to a stand-alone function and vice-versa. + +**Example** + +```typescript +class MyClass { + myMethod(arg: unknown) { + console.log("myMethod", arg); + } +} +var myFunction = function(arg: unknown) { + console.log("myFunction", arg); +}; +const c = new MyClass(); + +c.myMethod = myFunction; +c.myMethod("foo"); // should output: myFunction foo +// or +myFunction = c.myMethod; +myFunction("foo"); // should output: myMethod foo; +``` + +If `myFunction` did not have the initial parameter, calling either after being re-assigned would cause potential runtime errors, since `myMethod` would expect an initial parameter and `myFunction` would not. + +Note that even declared functions are assumed to have this extra parameter as well. + +**Example** + +```typescript +declare function myLibFunction(arg: unknown): void; +myLibFunction("foo"); +``` + +```lua +myLibFunction(nil, "foo") +``` + +# Removing the Context Parameter + +When dealing with external library functions that don't expect this initial parameter, you will need to inform TypeScriptToLua. This can be done a few different ways. + +## `this: void` + +You can declare any function with `this: void` to prevent generation of this initial argument. + +**Example** + +```typescript +declare function myLibFunction(this: void, arg: unknown): void; +myLibFunction("foo"); +``` + +```lua +myLibFunction("foo") +``` + +This works on methods as well, which can be useful if you have class methods which should be called with a dot `.` instead of a colon `:`. + +**Example** + +```typescript +declare class MyClass { + myMethodWithContext(arg: unknown): void; + myMethodWithoutContext(this: void, arg: unknown): void; +} +const c = new MyClass(); +c.myMethodWithContext("foo"); +c.myMethodWithoutContext("foo"); +``` + +```lua +local c = MyClass.new() +c:myMethodWithContext("foo") -- uses colon : +c.myMethodWithoutContext("foo") -- uses dot . +``` + +Another common scenario is a library function which takes a lua callback function, which should not have a context parameter. + +**Example** + +```typescript +type Callback = (this: void, arg: unknown) => void; +declare function takesCallback(this: void, callback: Callback): void; +takesCallback(arg => { + console.log(arg); +}); +``` + +```lua +takesCallback(function(arg) print(arg) end) +``` + +## `@noSelf` + +If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noself) directive. + +**Example** + +```typescript +/** @noSelf **/ +declare namespace MyNamespace { + export function myFunction(arg: unknown): void; +} +MyNamespace.myFunction("foo"); +``` + +```lua +MyNamespace.myFunction("foo") +``` + +You can override `@noSelf` on a per-function basis by specifying a `this` parameter. + +**Example** + +```typescript +/** @noSelf **/ +declare namespace MyNamespace { + export function myFunction(this: any, arg: unknown): void {} +} +MyNamespace.myFunction("foo"); +``` + +```lua +MyNamespace:myFunction("foo") +``` + +## `@noSelfInFile` + +If you want to specify that all functions in a file should have no context, you can use [`@noSelfInFile`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noselfinfile) at the top of the file. + +For more information on [`@noSelf`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noself) and [`@noSelfInFile`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noselfinfile), please refer to [Compiler Directives](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives). + +# Assignment Errors + +Functions that have a context parameter cannot be assigned to functions that do not, and vice-versa. A common case where this may occur is passing a callback to an api that expects a function that does not take an initial argument. + +**Example** + +```ts +declare function takesCallback(callback: (this: void, arg: string) => void); + +function myCallback(arg: string) {} +takesCallback(myCallback); // Error: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +``` + +This throws an error because if takesCallback called myCallback, it would do so without passing an initial context parameter. This can be easily fixed simply by wrapping the call in an arrow function. + +**Example** + +```typescript +takesCallback(arg => myCallback(arg)); +``` + +```lua +takesCallback(function(arg) return myCallback(nil, arg) end) +``` + +The reason this works is because TypeScriptToLua infers whether the arrow function should take a context parameter or not based on the type it's being assigned to. + +## Overloads + +If a function is overloaded and the signatures differ in context type, you can not assign them: + +```ts +declare function takesFunction(f: Function): void; + +declare function myFunction(this: void, s: string, n: number): void; +declare function myFunction(s: string); + +takesFunction(myFunction); // Error: Unsupported assignment of function with different overloaded types for 'this'. Overloads should all have the same type for 'this'. +``` + +It's best practice to avoid overloads with different context types. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..d05b1def --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,79 @@ +--- +title: Getting Started +--- + +This is a quick introduction into project setup and our CLI. +For a TypeScript quick start please read: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html + +## Installation + +NPM users can run: + +`npm install -g typescript-to-lua` + +## Project Setup + +We use the same configuration file that the vanilla TypeScript compiler `tsc` uses. +This file is called `tsconfig.json` and should be located in your projects root. + +Example: + +``` +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "strict": true + }, + "tstl": { + "luaTarget": "JIT" + } +} +``` + +Because we use the same configuration system `tsc` uses, you can use most of the [options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) available for vanilla TS (including source maps!) with some [limitations](https://github.com/Perryvw/TypescriptToLua/wiki/Limitations#config--compileroptions). + +In addition we add some Lua related options: + +### TSTL specific options + +| Option | Values | Description | +| ------------------ | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| luaTarget | "JIT", "5.3", "5.2", "5.1" (default: "JIT") | Specifies the Lua version you wan to generate code for. | +| noImplicitSelf | true, false (default: false) | If true, treats all project files as if they were prefixed with `/** @noSelfInFile **/`. | +| noHeader | true, false (default: false) | Set this to true if you don't want to include our header in the output. | +| luaLibImport | "inline" lualib functions
"require" lualib functions, "none" don't import lualib features | We polypill certain javascript features with Lua functions, this option specifies how these functions are imported into the Lua output. | +| sourceMapTraceback | true, false (default: false) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | +| luaBundle | Name/path of the output lua bundle file | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | +| luaBundleEntry | Name/path of the entry module (\*.ts) of your program | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | + +**IMPORTANT** These options need to be set in the `tstl` object of your `tsconfig.json`, do not set them inside `compilerOptions` (see example above). + +## Building your project + +Our command line interface is called `tstl` and it works almost exactly as TypeScript's `tsc`, you can pass `tsc` options to `tstl`. + +Example: + +`tstl -p pathToYour/tsconfig.json --luaTarget JIT --strict false` + +This command will build your project, overriding some options set in `tsconfig.json` (first example). + +## Compiling files directly + +Example: + +`tstl --luaTarget 5.1 --strict true script.ts` + +`tstl --luaTarget 5.1 --strict true script1.ts someDir/script2.ts` + +## Further Reading + +It's recommended to read the following articles, for more information on advanced usage and limitations of tstl. + +- [Differences between Lua and JS](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Differences-between-Lua-and-Javascript) +- [Writing Declarations](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Writing-Declarations) +- [Compiler Directives](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives) +- [Functions and the self Parameter](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Functions-and-the-self-Parameter) +- [Supported Lua Versions](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Supported-Lua-Versions) +- [Limitations](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Limitations) diff --git a/docs/limitations.md b/docs/limitations.md new file mode 100644 index 00000000..735d88b4 --- /dev/null +++ b/docs/limitations.md @@ -0,0 +1,71 @@ +--- +title: Limitations +--- + +There are certain features of Typescript, that are either not allowed or only partially supported. The following is a list of limitations. + +## Config / CompilerOptions + +Important Supported options are: + +- rootDir +- outDir +- project +- (baseUrl) +- skipLibCheck +- strict +- noImplicitAny +- noImplicitThis + +For options not listed here, as a rule of thumb: **all options that configure type checking will work.** + +All other options are most likely not supported and will have no effect. + +**baseUrl** + +Does only work if your base url points to a directory inside your rootDir. Avoid using baseUrl if possible. + +## String functions + +Not all string functions are supported. The supported functions are: + +- indexOf +- substring +- replace [[1](#stringreplace)] +- charAt +- split +- toLowerCase +- toUpperCase + +### string.replace + +Regular expressions in calls to `string.replace` are not supported. You can use Lua's Pattern strings (https://www.lua.org/pil/20.2.html) instead. + +## Array functions + +Not all array functions are supported. The supported functions are: + +- push +- forEach +- map +- filter +- some +- every +- slice +- splice +- join +- indexOf + +## Bit Operations + +Bit operations require Lua version `> 5.1` + +For `JIT` the LuaBitOp module (http://bitop.luajit.org) is required. + +## Try/Catch & Throw + +Basic support exists, but you are only allowed to throw string literals. + +## Iterating an array with `for ... in` + +Not allowed. diff --git a/docs/supported-lua-versions.md b/docs/supported-lua-versions.md new file mode 100644 index 00000000..30b7cb36 --- /dev/null +++ b/docs/supported-lua-versions.md @@ -0,0 +1,5 @@ +--- +title: Supported Lua Versions +--- + +We currently aim to support all 5.x versions of Lua, aswell as Lua JIT. diff --git a/docs/writing-declarations.md b/docs/writing-declarations.md new file mode 100644 index 00000000..67d7e2b3 --- /dev/null +++ b/docs/writing-declarations.md @@ -0,0 +1,639 @@ +--- +title: Writing Declarations +--- + +The real power of the transpiler is unlocked when combining it with declarations for your target environment. Declarations tell TypeScript which Lua API is available in your target context. + +If you need tips or help writing declarations, feel free to join our Discord: + +[https://discord.gg/BWAq58Y](https://discord.gg/BWAq58Y) + +#### Table of Contents + +- [About Declaration Files](#about-declaration-files) +- [Declare Keyword](#declare-keyword) +- [Export Keyword](#export-keyword) +- [Self Parameter](#self-parameter) +- [Comments and Directives](#comments-and-directives) +- [Environmental Declarations](#environmental-declarations) +- [Advanced Types](#advanced-types) +- [Declaration Merging](#declaration-merging) + - [Function + Table](#function-+-table) +- [Declaration Examples](#declaration-examples) + - [Interface](#interfaces) + - [Namespaces](#namespaces) + - [Classes](#classes) + - [Ambient Modules](#ambient-modules) + - [Unions](#unions) + - [Keyof](#keyof) + - [String Enums](#string-enums) + - [Keyword Workarounds](#keyword-workarounds) + - [Operator Overloads](#operator-overloads) + - [Import and Export](#import-and-export) +- [NPM Publishing](#npm-publishing) +- [Debugging Declarations](#debugging-declarations) + +## About Declaration Files + +Declaration files end with the extension `.d.ts`. These contain pure ambient code. + +> Ambient : relating to the immediate surroundings of something. + +For TypeScriptToLua, these files should contain information that describes the target Lua environment. + +This means functions, modules, variables and other members of the target Lua environment are primarily described in these files. + +They don't contain code that you would execute. Similar to how you'd write an interface in some other languages. TypeScriptToLua doesn't output any information from these files either. + +> Note: You can write ambient declarations inside `.ts` files as well. + +[Back to top](#table-of-contents) + +## Declare Keyword + +The _declare_ keyword is used to say that the following declaration defines something that exists within global scope. Like something within the _\_G_ table in Lua. + +This is useful for defining Lua's environment. + +```ts +// _G.d.ts +// Uses some declarations from +// https://www.lua.org/manual/5.1/manual.html + +/** + * A global variable (not a function) that holds a string containing the + * current interpreter version. + */ +declare const _VERSION: number; + +/** + * Receives any number of arguments, and prints their values to stdout, using the + * tostring function to convert them to strings. print is not intended for + * formatted output, but only as a quick way to show a value, typically for + * debugging. For formatted output, use string.format. + * @param args Arguments to print + */ +declare function print(...args: any[]): void; +``` + +```ts +// main.ts +print(_VERSION); // Editor and transpiler know what print and _VERSION are +``` + +> Note: You can use _declare_ to write ambient declarations inside `.ts` files. + +[Back to top](#table-of-contents) + +## Export Keyword + +The export keyword indicates something is exported and can be used by external code. + +This also includes ambient interfaces, types, modules and other items that don't result in any transpiled code. + +If a file named _lib.lua_ exists and returns a table with an _x_ field, you can write _lib.d.t.s_ as follows to tell TypeScript that _lib_ exists and what it provides. + +```ts +// lib.d.ts +export let x: number; +``` + +```ts +// main.ts +import { x } from "./lib"; +``` + +If a namespace contains certain functions, `export` tells TypeScript that those functions can be accessed within the namespace. + +```ts +// .d.ts +declare namespace table { + /** + * @noSelf + */ + export function insert(table: object, item: any): number; +} +``` + +```ts +// .ts +table.insert({}, 1); +``` + +If a globally available module exists within the Lua environment. You can define what the module provides. + +```ts +// .d.ts +declare module "utf8" { + /** + * @noSelf + */ + export function codepoint(): void; +} +``` + +```ts +import * as utf8 from "utf8"; // equiv to `local utf8 = require("utf8"); +utf8.codepoint(); +``` + +The `export` keyword can be used in a `.ts` or `.d.ts` file. It tells the transpiler and your editor (potentially) that something **contains/provides** something that you can either import (by using `import` in TS or `require()` in Lua) or access. + +[Back to top](#table-of-contents) + +## Self Parameter + +TypeScript has a hidden _this_ parameter attached to every function. + +This causes TypeScriptToLua to treat every function as if _self_ exists as its first parameter. + +```ts +declare function assert(value: any): void; +// TypeScript: assert(this: any, value: any): void; +// TypeScriptToLua: assert(self, value) +assert(true); // assert(_G, true) +``` + +This allows users to modify _this_ inside a function and expect behaviour similar to what JavaScript does. + +But obviously Lua does not have a _self_ parameter for every function, so one of the three options must happen to tell TypeScriptToLua there is no "contextual parameter" (_self_): + +1. Use `this: void` as the first parameter of the function / method. This formally describes to TypeScript to not allow _this_ to be modified inside this function. (you could also use the _--noImplicitThis_ option to disallow _this_ to be modified if _this_ is of an _any_ type). +2. Use `@noSelf` in the comments of the declaration's owner (the namespace, module, object, etc). +3. Use `@noSelfInFile` at the beginning of the file in a comment to make sure every function defined in this file does not use a "contextual parameter". + +Below is three ways to make _table.remove_ not use a "contextual parameter". + +```ts +declare namespace table { + export function remove(this: void, table: object, index: number): any; +} +``` + +```ts +/** @noSelf */ +declare namespace table { + export function remove(table: object, index: number): any; +} +``` + +```ts +/** @noSelfInFile */ + +declare namespace table { + export function remove(this: void, table: object, index: number): any; +} +``` + +> By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. + +[Back to top](#table-of-contents) + +## Comments and Directives + +If you're using an editor that seeks out information about functions, variables, etc. It will likely find the file where what it is analyzing is defined and check out the comment above it. + +```ts +// print.d.ts + +/** + * When hovering over print, this description will be shown + * @param {any[]} args Stuff to print + */ +declare function print(...args: any[]); +``` + +[Try out what this looks like in an editor]() + +TypeScript uses TSDoc for its comments. TSDoc allows you to also use markdown in your comments! This means pictures, links, tables, code syntax highlighting and more markdown features are available. These may display differently depending on the editor in use. + +Here are some commonly used TSDoc tags used in comments: + +| Tag | Description | +| -------------------------------------- | ---------------------------------------------------- | +| `@param ` | Defines a parameter. e.g. A parameter for a function | +| `@return ` | Describes the return value of a function / method | + +TypeScriptToLua takes this further. Some "tags" change how the transpiler translates certain pieces of code. These are referred to as _Directives_. + +As an example, `@tupleReturn` marks a function as something which returns multiple values instead of its array. + +```ts +/** + * Returns multiple values + * @tupleReturn + */ +declare function tuple(): [number, number]; + +let [a, b] = tuple(); +// local a, b = tuple() +``` + +```ts +/** + * Returns a table array containing two numbers + */ +declare function array(): [number, number]; + +let [c, d] = array(); +// local c, d = unpack(array()) +``` + +See [Compiler Directives wiki page](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives) for more directive info. + +[Back to top](#table-of-contents) + +## Environmental Declarations + +With TypeScript, by default, there are declarations that exist that describe something that doesn't exist in Lua (like `console.log`). + +Using the _lib_ option can narrow down these declarations. + +```json +{ + "compilerOptions": { + "lib": ["esnext"] + } +} +``` + +It is possible to also use _noLib_ to remove every declaration but TypeScript NEEDS certain declarations to exist so they will have to be manually defined. TypeScriptToLua also treats certain declarations differently specifically if they came from the standard libs. So _noLib_ is not recommended. + +## Advanced Types + +We recommend reading about Mapped and Conditional types. These things can be used as effective tools to describe some dynamic things that you may have in Lua. + +- [Advanced Types (TypeScriptLang)](https://www.typescriptlang.org/docs/handbook/advanced-types.html#) + - [Mapped Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types) + - [Conditional Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types) + +## Declaration Merging + +https://www.typescriptlang.org/docs/handbook/declaration-merging.html + +Some examples of declaration merging have been shown in the above examples. + +### Function + Table + +Some tables can use `__call` to make themselves callable. Busted (the Lua testing suite) does this to `assert`. + +```ts +// .d.ts +declare namespace assert { + export function isEqual(): void; +} +declare function assert(value: any, errorDescription?: string): void; +``` + +```ts +assert.isEqual(); +assert(); +``` + +[Back to top](#table-of-contents) + +## Declaration Examples + +### Interfaces + +```ts +// .d.ts +interface Image { + /** @tupleReturn */ + getDimensions(): [number, number]; +} + +// This interface merges with its previous declaration +/** @noSelf */ +interface Image { + getFlags(): object; +} +``` + +```ts +// usage.ts +declare let image: Image; +let [w, h] = image.getDimensions(); // local w, h = image:getDimensions() +let o = image.getFlags(); +``` + +[Back to top](#table-of-contents) + +### Namespaces + +```ts +// .d.ts +declare namespace love { + export let update: (delta: number) => void; + /** @tupleReturn */ + export function getVersion(delta: number): [number, number, number, string]; + export namespace graphics { + function newImage(filename: string): Image; + } +} + +// This namespace merges with its previous declaration +/** @noSelf */ +declare namespace love { + export let update: (delta: number) => void; +} + +/** @noSelf */ +declare namespace string { + function byte(s: string, i?: number, j?: number): number; +} +``` + +```ts +// usage.ts +let [a, b, c, d] = love.getVersion(); +let p = love.graphics.newImage("file.png"); +``` + +[Back to top](#table-of-contents) + +### Classes + +You'd only declare these if there were TypeScriptToLua compatible classes within the existing Lua code. It is definitely not recommended to define classes in ambient contexts for TypeScriptToLua, use interfaces instead. + +```ts +// .d.ts +declare class X { + tuple(); +} +``` + +```ts +// usage.ts +let p = new X(); +p.tuple(); +``` + +[Back to top](#table-of-contents) + +### Ambient Modules + +You may have to use the `@noResolution` directive to tell TypeScriptToLua to not try any path resolution methods when the specified module is imported. + +Module declarations need to be kept in _.d.ts_ files. + +```ts +// .d.ts +/** @noSelf */ +declare module "image-size" { + export function getimagewidth(filename: string): number; + export function getimageheight(filename: string): number; +} + +/** + * A module that only contains a number + * @noResolution + */ +declare module "number-of-the-day" { + let x: number; + export = x; +} + +/** + * Not very useful for TypeScript. It has no idea what is in here. + * @noResolution + */ +declare module "custommodule"; +``` + +```ts +// .ts +import { getimagewidth, getimageheight } from "image-size"; +import * as x from "contains_a_number"; +import * as custommodule from "custommodule"; +``` + +### Unions + +Unions can be used to tell TypeScript that a given type could be one of many other types. TypeScript can then pick up hints in the code to figure out what that type is at a given statement. + +```ts +// .ts +declare interface PingResponse { + type: "ping"; + timeTaken: number; +} + +declare interface MessageResponse { + type: "message"; + text: string; +} + +declare type Response = PingResponse | MessageResponse; + +declare let response: Response; + +response.timeTaken; +// Not allowed, if response is a MessageResponse, it won't have a timeTaken field + +switch (response.type) { + case "ping": + return response.timeTaken; + // If the program arrives here, response: PingResponse + case "message": + return response.text; + // If the program arrives here, response: MessageResponse + case "disconnect": // Impossible + default: + // Because of what Response is described as, TypeScript knows getting + // here is impossible. +} +``` + +[Back to top](#table-of-contents) + +### keyof + +```ts +declare interface AvailableFiles { + "player.png": any; + "file.txt": any; +} + +declare function getFile(filename: keyof AvailableFiles): string; + +getFile("player.png"); // Valid +getFile("unknown.png"); // Invalid +``` + +[Back to top](#table-of-contents) + +### String Enums + +TypeScript can check a string is valid. + +```ts +declare function draw(linetype: "solid" | "dashed", x1, y1, x2, y2): void; +draw("solid", 0, 0, 16, 16); // Valid +draw("rounded", 0, 0, 16, 16); // Invalid +``` + +> This can apply to numbers as well + +[Back to top](#table-of-contents) + +### Keyword Workarounds + +Some functions in Lua can have names that are keywords in TypeScript (e.g., _try_, _catch_, _new_, etc). + +The parent to these kinds of functions will need to be represented as a JSON object. + +```ts +// ❌ +declare namespace table { + export function new: () => any; +} + +// ✔ +declare let table: { + new: () => any; +} +``` + +```ts +// ❌ +declare module "creator" { + export function new: () => any; +} + +// ✔ +declare module "creator" { + let exports: { + new: () => any; + } + export = exports; +} +``` + +[Back to top](#table-of-contents) + +### Operator Overloads + +Lua supports overloading of mathematical operators such as `+`, `-` or `*`. Since TypeScript does not support operator overloading in its type system this is hard to replicate. Unfortunately this is not something that can be fixed properly right now without forking off our custom TypeScript version. + +There is however a workaround that works decently: If you declare a type as intersection type with number it will inherit all mathematical operators. For example: + +```ts +declare type Vector = number & { + x: number; + y: number; + dot(v: Vector): number; + cross(v: Vector): Vector; +}; + +declare function Vector(x: number, y: number): Vector; + +const v1 = Vector(3, 4); +const v2 = Vector(4, 5); +const v3 = (v1 * 4) as Vector; +const d = v3.dot(v2); +``` + +[Back to top](#table-of-contents) + +### Import and export + +Using `import` can be important for making sure an _index.d.ts_ file contains all the declarations needed. + +```ts +import "./lib"; +// All declarations in lib will be included with this file + +export { Player } from "./Entities"; +// The Player declaration is re-exported from this file +``` + +It is also possible to place _import_ statements inside ambient modules and namespaces. + +```ts +declare module "mymodule" { + import * as types from "types"; + export function getType(): types.Type; +} +``` + +[Back to top](#table-of-contents) + +## NPM Publishing + +It is possible to publish a list of declarations for other users to easily download via _npm_. + +```bash +npm init +npm login # Need npm account +npm publish --dry-run # Show what files will be published +npm version 0.0.1 # Update the version in package.json when --dry-run seems good +npm publish # Publish to npm (only if you're 100% sure) +``` + +Then the user can install this package using: + +```bash +npm install --save-dev +``` + +And link it to a _tsconfig.json_ file. + +```json +{ + "compilerOptions": { + "types": { + "declarations" + } + } +} +``` + +[Back to top](#table-of-contents) + +## Debugging Declarations + +If you have TypeScript installed, you can use the command below to list all files a _tsconfig.json_ file targets. + +``` +tsc -p tsconfig.json --noEmit --listFiles +``` + +This only works with TypeScript (_tsc_). TypeScriptToLua (_tstl_) may have support for this in the future. + +Every TypeScript project points to a list of declarations. TypeScript is very generous with what files that includes. + +```json +{ + "compilerOptions": { + "rootDir": "src" + } +} +``` + +```diff + node_modules/ ++ src/main.ts ++ src/actors/Player.ts ++ global.ts + tsconfig.json +``` + +```json +{ + "compilerOptions": { + "rootDir": "src", + "types": ["lua-types/jit"] + } +} +``` + +```diff ++ node_modules/lua-types/jit.d.ts ++ src/main.ts ++ src/actors/Player.ts ++ global.ts + tsconfig.json +``` + +[Back to top](#table-of-contents) diff --git a/docusaurus.config.js b/docusaurus.config.js index f58a38c3..d93622b9 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -11,6 +11,7 @@ module.exports = { title: "TypeScriptToLua", logo: { src: "images/logo.png" }, links: [ + { to: "docs/getting-started", label: "Docs", position: "left" }, { to: "play", label: "Playground", position: "left" }, { href: "https://discord.gg/BWAq58Y", label: "Discord", position: "right" }, { href: "https://github.com/TypeScriptToLua/TypeScriptToLua", label: "GitHub", position: "right" }, @@ -25,6 +26,10 @@ module.exports = { [ "@docusaurus/preset-classic", { + docs: { + sidebarPath: require.resolve("./sidebars.json"), + editUrl: "https://github.com/TypeScriptToLua/TypeScriptToLua.github.io/edit/source/", + }, theme: { customCss: require.resolve("./src/custom.scss"), }, diff --git a/sidebars.json b/sidebars.json new file mode 100644 index 00000000..3b11212b --- /dev/null +++ b/sidebars.json @@ -0,0 +1,21 @@ +{ + "docs": [ + "getting-started", + "differences-between-lua-and-javascript", + "writing-declarations", + "compiler-annotations", + "functions-and-the-self-parameter", + "supported-lua-versions", + "limitations", + { + "type": "category", + "label": "API", + "items": ["api/overview", "api/transformer", "api/printer"] + }, + { + "type": "category", + "label": "Contributors", + "items": ["contributors/design-goals"] + } + ] +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 337d8856..53f14ae1 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -88,6 +88,12 @@ export default function Home() {

Write Lua with TypeScript

+ + Get Started + Try Online From 640d83993c49ae22acf4cc6c1a3ac35d16a83d45 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 26 Feb 2020 01:13:37 +0000 Subject: [PATCH 02/23] Minor style changes --- README.md | 4 +- docs/compiler-annotations.md | 17 ----- .../differences-between-lua-and-javascript.md | 16 ++-- docs/getting-started.md | 52 +++++++------ docs/writing-declarations.md | 73 ++----------------- 5 files changed, 45 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index bb31abc5..d5450137 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern ### Installation -``` +```bash $ npm install ``` ### Local Development -``` +```bash $ npm run start ``` diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md index 678bcd02..ab4167ad 100644 --- a/docs/compiler-annotations.md +++ b/docs/compiler-annotations.md @@ -4,23 +4,6 @@ title: Compiler Annotations To improve translation and compatibility to different Lua interfaces, the TypscriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax. -#### Table of Contents - -- [@compileMembersOnly](#compilemembersonly) -- [@customConstructor](#customconstructor) -- [@extension](#extension) -- [@forRange](#forRange) -- [@luaIterator](#luaIterator) -- [@luaTable](#luaTable) -- [@metaExtension](#metaextension) -- [@noResolution](#noResolution) -- [@noSelf](#noself) -- [@noSelfInFile](#noselfinfile) -- [@phantom](#phantom) -- [@pureAbstract](#pureabstract) -- [@tupleReturn](#tuplereturn) -- [@vararg](#vararg) - ## @compileMembersOnly **Target elements:** `(declare) enum` diff --git a/docs/differences-between-lua-and-javascript.md b/docs/differences-between-lua-and-javascript.md index 77133db2..d32e3e45 100644 --- a/docs/differences-between-lua-and-javascript.md +++ b/docs/differences-between-lua-and-javascript.md @@ -7,25 +7,25 @@ This project aims for both compilation results to have the same behavior as much Below are some of the cases where resulting Lua intentionally behaves different from compiled JS. -# True/False values +# [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. Therefore there is also no difference between `==` and `===` when compiling to Lua, all comparisons are strict (`===`). | TypeScript | _JavaScript behavior_ | _Lua behavior_ | | ----------------- | --------------------- | -------------- | | `false` | `false` | `false` | -| `undefined` | `false` | `nil`→ `false` | -| `null` | `false` | `nil`→ `false` | -| `NaN` | `false` | `nil`→ `false` | -| `""` | ⚠️`false` | ⚠️`true` | -| `0` | ⚠️`false` | ⚠️`true` | -| (Everything else) | `false` | `false` | +| `undefined` | `false` | `false` | +| `null` | `false` | `false` | +| `NaN` | `false` | ⚠️`true` | +| `""` | `false` | ⚠️`true` | +| `0` | `false` | ⚠️`true` | +| (Everything else) | `true` | `true` | # List Length List length is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between Javascript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`. -##Exmples: +## Examples: **Safe (no difference):** diff --git a/docs/getting-started.md b/docs/getting-started.md index d05b1def..639ef240 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -9,7 +9,9 @@ For a TypeScript quick start please read: https://www.typescriptlang.org/docs/ha NPM users can run: -`npm install -g typescript-to-lua` +```bash +npm install -g typescript-to-lua +``` ## Project Setup @@ -18,16 +20,16 @@ This file is called `tsconfig.json` and should be located in your projects root. Example: -``` +```json { - "compilerOptions": { - "target": "esnext", - "lib": ["esnext"], - "strict": true - }, - "tstl": { - "luaTarget": "JIT" - } + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "strict": true + }, + "tstl": { + "luaTarget": "JIT" + } } ``` @@ -37,15 +39,15 @@ In addition we add some Lua related options: ### TSTL specific options -| Option | Values | Description | -| ------------------ | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| luaTarget | "JIT", "5.3", "5.2", "5.1" (default: "JIT") | Specifies the Lua version you wan to generate code for. | -| noImplicitSelf | true, false (default: false) | If true, treats all project files as if they were prefixed with `/** @noSelfInFile **/`. | -| noHeader | true, false (default: false) | Set this to true if you don't want to include our header in the output. | -| luaLibImport | "inline" lualib functions
"require" lualib functions, "none" don't import lualib features | We polypill certain javascript features with Lua functions, this option specifies how these functions are imported into the Lua output. | -| sourceMapTraceback | true, false (default: false) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | -| luaBundle | Name/path of the output lua bundle file | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | -| luaBundleEntry | Name/path of the entry module (\*.ts) of your program | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | +| Option | Values | Description | +| -------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `luaTarget` | `"JIT"`, `"5.3"`, `"5.2"`, `"5.1"` (default: `"JIT"`) | Specifies the Lua version you wan to generate code for. | +| `noImplicitSelf` | `true`, `false` (default: `false`) | If true, treats all project files as if they were prefixed with
`/** @noSelfInFile **/`. | +| `noHeader` | `true`, `false` (default: `false`) | Set this to true if you don't want to include our header in the output. | +| `luaLibImport` | `"inline"`, `"require"`, `"always"`, `"none"` (default: `"require"`) | We polyfill certain JavaScript features with Lua functions, this option specifies how these functions are imported into the Lua output. | +| `sourceMapTraceback` | `true`, `false` (default: `false`) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | +| `luaBundle` | File path (relative to the `tsconfig.json`) | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | +| `luaBundleEntry` | File path (relative to the `tsconfig.json`) | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | **IMPORTANT** These options need to be set in the `tstl` object of your `tsconfig.json`, do not set them inside `compilerOptions` (see example above). @@ -55,7 +57,9 @@ Our command line interface is called `tstl` and it works almost exactly as TypeS Example: -`tstl -p pathToYour/tsconfig.json --luaTarget JIT --strict false` +```bash +tstl -p pathToYour/tsconfig.json --luaTarget JIT --strict false +``` This command will build your project, overriding some options set in `tsconfig.json` (first example). @@ -63,9 +67,13 @@ This command will build your project, overriding some options set in `tsconfig.j Example: -`tstl --luaTarget 5.1 --strict true script.ts` +```bash +tstl --luaTarget 5.1 --strict true script.ts +``` -`tstl --luaTarget 5.1 --strict true script1.ts someDir/script2.ts` +```bash +tstl --luaTarget 5.1 --strict true script1.ts someDir/script2.ts +``` ## Further Reading diff --git a/docs/writing-declarations.md b/docs/writing-declarations.md index 67d7e2b3..f749dd6b 100644 --- a/docs/writing-declarations.md +++ b/docs/writing-declarations.md @@ -4,34 +4,7 @@ title: Writing Declarations The real power of the transpiler is unlocked when combining it with declarations for your target environment. Declarations tell TypeScript which Lua API is available in your target context. -If you need tips or help writing declarations, feel free to join our Discord: - -[https://discord.gg/BWAq58Y](https://discord.gg/BWAq58Y) - -#### Table of Contents - -- [About Declaration Files](#about-declaration-files) -- [Declare Keyword](#declare-keyword) -- [Export Keyword](#export-keyword) -- [Self Parameter](#self-parameter) -- [Comments and Directives](#comments-and-directives) -- [Environmental Declarations](#environmental-declarations) -- [Advanced Types](#advanced-types) -- [Declaration Merging](#declaration-merging) - - [Function + Table](#function-+-table) -- [Declaration Examples](#declaration-examples) - - [Interface](#interfaces) - - [Namespaces](#namespaces) - - [Classes](#classes) - - [Ambient Modules](#ambient-modules) - - [Unions](#unions) - - [Keyof](#keyof) - - [String Enums](#string-enums) - - [Keyword Workarounds](#keyword-workarounds) - - [Operator Overloads](#operator-overloads) - - [Import and Export](#import-and-export) -- [NPM Publishing](#npm-publishing) -- [Debugging Declarations](#debugging-declarations) +If you need tips or help writing declarations, feel free to [join our Discord](https://discord.gg/BWAq58Y). ## About Declaration Files @@ -47,8 +20,6 @@ They don't contain code that you would execute. Similar to how you'd write an in > Note: You can write ambient declarations inside `.ts` files as well. -[Back to top](#table-of-contents) - ## Declare Keyword The _declare_ keyword is used to say that the following declaration defines something that exists within global scope. Like something within the _\_G_ table in Lua. @@ -83,8 +54,6 @@ print(_VERSION); // Editor and transpiler know what print and _VERSION are > Note: You can use _declare_ to write ambient declarations inside `.ts` files. -[Back to top](#table-of-contents) - ## Export Keyword The export keyword indicates something is exported and can be used by external code. @@ -139,8 +108,6 @@ utf8.codepoint(); The `export` keyword can be used in a `.ts` or `.d.ts` file. It tells the transpiler and your editor (potentially) that something **contains/provides** something that you can either import (by using `import` in TS or `require()` in Lua) or access. -[Back to top](#table-of-contents) - ## Self Parameter TypeScript has a hidden _this_ parameter attached to every function. @@ -187,8 +154,6 @@ declare namespace table { > By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. -[Back to top](#table-of-contents) - ## Comments and Directives If you're using an editor that seeks out information about functions, variables, etc. It will likely find the file where what it is analyzing is defined and check out the comment above it. @@ -241,8 +206,6 @@ let [c, d] = array(); See [Compiler Directives wiki page](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives) for more directive info. -[Back to top](#table-of-contents) - ## Environmental Declarations With TypeScript, by default, there are declarations that exist that describe something that doesn't exist in Lua (like `console.log`). @@ -290,8 +253,6 @@ assert.isEqual(); assert(); ``` -[Back to top](#table-of-contents) - ## Declaration Examples ### Interfaces @@ -317,8 +278,6 @@ let [w, h] = image.getDimensions(); // local w, h = image:getDimensions() let o = image.getFlags(); ``` -[Back to top](#table-of-contents) - ### Namespaces ```ts @@ -350,8 +309,6 @@ let [a, b, c, d] = love.getVersion(); let p = love.graphics.newImage("file.png"); ``` -[Back to top](#table-of-contents) - ### Classes You'd only declare these if there were TypeScriptToLua compatible classes within the existing Lua code. It is definitely not recommended to define classes in ambient contexts for TypeScriptToLua, use interfaces instead. @@ -369,8 +326,6 @@ let p = new X(); p.tuple(); ``` -[Back to top](#table-of-contents) - ### Ambient Modules You may have to use the `@noResolution` directive to tell TypeScriptToLua to not try any path resolution methods when the specified module is imported. @@ -445,8 +400,6 @@ switch (response.type) { } ``` -[Back to top](#table-of-contents) - ### keyof ```ts @@ -461,8 +414,6 @@ getFile("player.png"); // Valid getFile("unknown.png"); // Invalid ``` -[Back to top](#table-of-contents) - ### String Enums TypeScript can check a string is valid. @@ -475,8 +426,6 @@ draw("rounded", 0, 0, 16, 16); // Invalid > This can apply to numbers as well -[Back to top](#table-of-contents) - ### Keyword Workarounds Some functions in Lua can have names that are keywords in TypeScript (e.g., _try_, _catch_, _new_, etc). @@ -510,8 +459,6 @@ declare module "creator" { } ``` -[Back to top](#table-of-contents) - ### Operator Overloads Lua supports overloading of mathematical operators such as `+`, `-` or `*`. Since TypeScript does not support operator overloading in its type system this is hard to replicate. Unfortunately this is not something that can be fixed properly right now without forking off our custom TypeScript version. @@ -534,8 +481,6 @@ const v3 = (v1 * 4) as Vector; const d = v3.dot(v2); ``` -[Back to top](#table-of-contents) - ### Import and export Using `import` can be important for making sure an _index.d.ts_ file contains all the declarations needed. @@ -557,8 +502,6 @@ declare module "mymodule" { } ``` -[Back to top](#table-of-contents) - ## NPM Publishing It is possible to publish a list of declarations for other users to easily download via _npm_. @@ -581,21 +524,17 @@ And link it to a _tsconfig.json_ file. ```json { - "compilerOptions": { - "types": { - "declarations" - } - } + "compilerOptions": { + "types": ["declarations"] + } } ``` -[Back to top](#table-of-contents) - ## Debugging Declarations If you have TypeScript installed, you can use the command below to list all files a _tsconfig.json_ file targets. -``` +```bash tsc -p tsconfig.json --noEmit --listFiles ``` @@ -635,5 +574,3 @@ Every TypeScript project points to a list of declarations. TypeScript is very ge + global.ts tsconfig.json ``` - -[Back to top](#table-of-contents) From cca9027a5535bbea00c63d01aee22214add91ab2 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 26 Feb 2020 07:43:34 +0000 Subject: [PATCH 03/23] Add custom `CodeBlock` component with file name and playground button --- docs/getting-started.md | 2 +- docs/writing-declarations.md | 8 +- src/pages/play/code.ts | 4 + src/theme/CodeBlock/index.js | 140 +++++++++++++++++++++++++ src/theme/CodeBlock/styles.module.scss | 54 ++++++++++ 5 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 src/theme/CodeBlock/index.js create mode 100644 src/theme/CodeBlock/styles.module.scss diff --git a/docs/getting-started.md b/docs/getting-started.md index 639ef240..f992937f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -20,7 +20,7 @@ This file is called `tsconfig.json` and should be located in your projects root. Example: -```json +```json title=tsconfig.json { "compilerOptions": { "target": "esnext", diff --git a/docs/writing-declarations.md b/docs/writing-declarations.md index f749dd6b..8c5a9f33 100644 --- a/docs/writing-declarations.md +++ b/docs/writing-declarations.md @@ -212,7 +212,7 @@ With TypeScript, by default, there are declarations that exist that describe som Using the _lib_ option can narrow down these declarations. -```json +```json title=tsconfig.json { "compilerOptions": { "lib": ["esnext"] @@ -522,7 +522,7 @@ npm install --save-dev And link it to a _tsconfig.json_ file. -```json +```json title=tsconfig.json { "compilerOptions": { "types": ["declarations"] @@ -542,7 +542,7 @@ This only works with TypeScript (_tsc_). TypeScriptToLua (_tstl_) may have suppo Every TypeScript project points to a list of declarations. TypeScript is very generous with what files that includes. -```json +```json title=tsconfig.json { "compilerOptions": { "rootDir": "src" @@ -558,7 +558,7 @@ Every TypeScript project points to a list of declarations. TypeScript is very ge tsconfig.json ``` -```json +```json title=tsconfig.json { "compilerOptions": { "rootDir": "src", diff --git a/src/pages/play/code.ts b/src/pages/play/code.ts index 8cc4f740..b3ceff1f 100644 --- a/src/pages/play/code.ts +++ b/src/pages/play/code.ts @@ -39,3 +39,7 @@ export function updateCodeHistory(code: string) { const hash = `code/${lzstring.compressToEncodedURIComponent(code)}`; window.history.replaceState({}, "", `#${hash}`); } + +export function getPlaygroundUrlForCode(code: string) { + return `/play/#code/${lzstring.compressToEncodedURIComponent(code)}`; +} diff --git a/src/theme/CodeBlock/index.js b/src/theme/CodeBlock/index.js new file mode 100644 index 00000000..9ae78627 --- /dev/null +++ b/src/theme/CodeBlock/index.js @@ -0,0 +1,140 @@ +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import useThemeContext from "@theme/hooks/useThemeContext"; +import classnames from "classnames"; +import Clipboard from "clipboard"; +import rangeParser from "parse-numeric-range"; +import Highlight, { defaultProps } from "prism-react-renderer"; +import defaultTheme from "prism-react-renderer/themes/palenight"; +import React, { useEffect, useRef, useState } from "react"; +import { getPlaygroundUrlForCode } from "../../pages/play/code"; +import styles from "./styles.module.scss"; + +function useClipboard() { + const target = useRef(null); + const button = useRef(null); + + const [showCopied, setShowCopied] = useState(false); + useEffect(() => { + let clipboard; + + if (button.current) { + clipboard = new Clipboard(button.current, { + target: () => target.current, + }); + } + + return () => { + if (clipboard) { + clipboard.destroy(); + } + }; + }, [button.current, target.current]); + + const handleCopyCode = () => { + window.getSelection().empty(); + setShowCopied(true); + + setTimeout(() => setShowCopied(false), 2000); + }; + + return { showCopied, handleCopyCode, target, button }; +} + +function usePrismTheme(prism) { + const [mounted, setMounted] = useState(false); + // The Prism theme on SSR is always the default theme but the site theme + // can be in a different mode. React hydration doesn't update DOM styles + // that come from SSR. Hence force a re-render after mounting to apply the + // current relevant styles. There will be a flash seen of the original + // styles seen using this current approach but that's probably ok. Fixing + // the flash will require changing the theming approach and is not worth it + // at this point. + useEffect(() => { + setMounted(true); + }, []); + + const { isDarkTheme } = useThemeContext(); + const lightModeTheme = prism.theme || defaultTheme; + const darkModeTheme = prism.darkTheme || lightModeTheme; + const prismTheme = isDarkTheme ? darkModeTheme : lightModeTheme; + return { prismTheme, mounted }; +} + +export default ({ children, className: languageClassName, metastring = "" }) => { + const { + siteConfig: { + themeConfig: { prism = {} }, + }, + } = useDocusaurusContext(); + + const { prismTheme, mounted } = usePrismTheme(prism); + const { showCopied, handleCopyCode, target, button } = useClipboard(); + + const code = children.trim(); + const [, title] = metastring.match(/title=(.+)( |$)/) ?? []; + + const [, highlightLinesRange] = metastring.match(/{([\d,-]+)}/) ?? []; + const highlightLines = highlightLinesRange != null ? rangeParser.parse(highlightLinesRange).filter(n => n > 0) : []; + + let language = languageClassName && languageClassName.replace(/language-/, ""); + if (!language && prism.defaultLanguage) { + language = prism.defaultLanguage; + } + + const hasPlayground = language === "ts" || language === "typescript"; + + return ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( + <> + {title &&
{title}
} +
+                        
+
+                        {hasPlayground && (
+                            
+                                Playground
+                            
+                        )}
+
+                        
+                            {tokens.map((line, i) => {
+                                if (line.length === 1 && line[0].content === "") {
+                                    line[0].content = "\n";
+                                }
+
+                                const lineProps = getLineProps({ line, key: i });
+
+                                if (highlightLines.includes(i + 1)) {
+                                    lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
+                                }
+
+                                return (
+                                    
+ {line.map((token, key) => ( + + ))} +
+ ); + })} +
+
+ + )} +
+ ); +}; diff --git a/src/theme/CodeBlock/styles.module.scss b/src/theme/CodeBlock/styles.module.scss new file mode 100644 index 00000000..9246ea7a --- /dev/null +++ b/src/theme/CodeBlock/styles.module.scss @@ -0,0 +1,54 @@ +.codeBlock { + overflow: auto; + display: block; + padding: 0; + margin: 0; + + &.hasTitle { + padding-top: 16px; + } +} + +.title { + position: absolute; + left: 8px; + padding: 3px 5px; + border-radius: 0 0 10% 10%; + background-color: var(--ifm-color-primary); + color: white; +} + +.codeBlockLines { + background-color: transparent; + border-radius: 0; + margin-bottom: 0; + float: left; + min-width: 100%; + padding: var(--ifm-pre-padding); +} + +.copyButton, +.playgroundButton { + background: rgb(1, 22, 39); + border: 1px solid rgb(214, 222, 235); + border-radius: var(--ifm-global-radius); + color: rgb(214, 222, 235); + cursor: pointer; + line-height: 12px; + opacity: 0; + outline: none; + padding: 4px 8px; + position: absolute; + right: var(--ifm-pre-padding); + top: var(--ifm-pre-padding); + visibility: hidden; + transition: opacity 200ms ease-in-out, visibility 200ms ease-in-out, bottom 200ms ease-in-out; + .codeBlock:hover > & { + visibility: visible; + opacity: 1; + } +} + +.playgroundButton { + top: calc(var(--ifm-pre-padding) + 28px); +} From f9cb634e2532715452ef4172570e34983d28d806 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 26 Feb 2020 09:06:17 +0000 Subject: [PATCH 04/23] Update relative documentation links --- docs/api/overview.md | 4 ++-- docs/api/printer.md | 2 +- docs/compiler-annotations.md | 2 +- docs/contributors/design-goals.md | 6 +++--- docs/functions-and-the-self-parameter.md | 6 +++--- docs/getting-started.md | 13 +------------ docs/writing-declarations.md | 4 ++-- 7 files changed, 13 insertions(+), 24 deletions(-) diff --git a/docs/api/overview.md b/docs/api/overview.md index de5f4df8..95ddae06 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -100,8 +100,8 @@ The low-level API consists of only one function: `transpile`. It takes a TypeScr More information on extending the transformer and printer can be found here: -- [Custom LuaTransformer API](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaTransformer-API) -- [Custom LuaPrinter API](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaPrinter-API) +- [Custom LuaTransformer API](transformer.md) +- [Custom LuaPrinter API](printer.md) **Arguments:** diff --git a/docs/api/printer.md b/docs/api/printer.md index 76bfce67..c8b8e7bf 100644 --- a/docs/api/printer.md +++ b/docs/api/printer.md @@ -6,7 +6,7 @@ The [LuaPrinter](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/ ## Visitor Pattern -Like the LuaTransformer, the LuaPrinter class also implements a visitor pattern. For more explanation see the [visitor pattern explanation on the LuaTransformer page](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Custom-LuaTransformer-API#visitor-pattern) +Like the LuaTransformer, the LuaPrinter class also implements a visitor pattern. For more explanation see the [visitor pattern explanation on the LuaTransformer page](transformer.md#visitor-pattern) ## API Reference diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md index ab4167ad..46e3f983 100644 --- a/docs/compiler-annotations.md +++ b/docs/compiler-annotations.md @@ -261,7 +261,7 @@ NormalNS.normalFunc("foo"); Translates to NormalNS:norm NoSelfNS.noSelfFunc("bar"); ============> NoSelfNS.noSelfFunc("bar") ``` -For more information about how the `self` parameter is handled, see [Functions and the `self` Parameter](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Functions-and-the-%60self%60-Parameter) +For more information about how the `self` parameter is handled, see [Functions and the `self` Parameter](functions-and-the-self-parameter.md) ## @noSelfInFile diff --git a/docs/contributors/design-goals.md b/docs/contributors/design-goals.md index 8769bdc9..b93c772a 100644 --- a/docs/contributors/design-goals.md +++ b/docs/contributors/design-goals.md @@ -10,15 +10,15 @@ Ofcourse there are several nuances to be taken into account here, as will be dis ## Declarations First -The main goal of this project is for it to be applicable to any Lua environment and API. This means the process of declaring platform APIs and existing Lua functionality are central in the design of the transpiler. As a result of this, TypeScriptToLua supports several [Compiler directives](https://github.com/Perryvw/TypescriptToLua/wiki/Compiler-Directives) that affect the resulting Lua to better match API declarations. +The main goal of this project is for it to be applicable to any Lua environment and API. This means the process of declaring platform APIs and existing Lua functionality are central in the design of the transpiler. As a result of this, TypeScriptToLua supports several [Compiler Annotations](../compiler-annotations.md) that affect the resulting Lua to better match API declarations. ## Functional equivalence Lua/JavaScript Code written in TypeScript can be transpiled into both JavaScript and Lua. It would therefore be reasonable to expect that two programs with the same source are functionally equivalent: this is **NOT** the case. -We aim to keep the functionality between transpiled JavaScript and Lua equivalent as much as possible. However, since JavaScript and Lua are fundamentally differences, equivalence of functionality can not be guaranteed. See [Differences between Lua and JavaScript](https://github.com/Perryvw/TypescriptToLua/wiki/Differences-between-Lua-and-Javascript) for more details. +We aim to keep the functionality between transpiled JavaScript and Lua equivalent as much as possible. However, since JavaScript and Lua are fundamentally differences, equivalence of functionality can not be guaranteed. See [Differences between Lua and JavaScript](../differences-between-lua-and-javascript.md) for more details. -We will try to stay functionally equivalent as much as possible, but not at all costs. If the workaround needed to support equivalent JavaScript functionality is too complex or flawed, we may accept slightly different functionality in Lua compared to JavaScript. A list of such limitations can be found here: [Limitations](https://github.com/Perryvw/TypescriptToLua/wiki/Limitations). +We will try to stay functionally equivalent as much as possible, but not at all costs. If the workaround needed to support equivalent JavaScript functionality is too complex or flawed, we may accept slightly different functionality in Lua compared to JavaScript. A list of such limitations can be found here: [Limitations](../limitations.md). ## Optimization strategy diff --git a/docs/functions-and-the-self-parameter.md b/docs/functions-and-the-self-parameter.md index 42e00b15..6e2da822 100644 --- a/docs/functions-and-the-self-parameter.md +++ b/docs/functions-and-the-self-parameter.md @@ -113,7 +113,7 @@ takesCallback(function(arg) print(arg) end) ## `@noSelf` -If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noself) directive. +If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](compiler-annotations.md#noself) directive. **Example** @@ -147,9 +147,9 @@ MyNamespace:myFunction("foo") ## `@noSelfInFile` -If you want to specify that all functions in a file should have no context, you can use [`@noSelfInFile`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noselfinfile) at the top of the file. +If you want to specify that all functions in a file should have no context, you can use [`@noSelfInFile`](compiler-annotations.md#noselfinfile) at the top of the file. -For more information on [`@noSelf`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noself) and [`@noSelfInFile`](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives#noselfinfile), please refer to [Compiler Directives](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives). +For more information on [`@noSelf`](compiler-annotations.md#noself) and [`@noSelfInFile`](compiler-annotations.md#noselfinfile), please refer to [Compiler Annotations](compiler-annotations). # Assignment Errors diff --git a/docs/getting-started.md b/docs/getting-started.md index f992937f..0d0e8999 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -33,7 +33,7 @@ Example: } ``` -Because we use the same configuration system `tsc` uses, you can use most of the [options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) available for vanilla TS (including source maps!) with some [limitations](https://github.com/Perryvw/TypescriptToLua/wiki/Limitations#config--compileroptions). +Because we use the same configuration system `tsc` uses, you can use most of the [options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) available for vanilla TS (including source maps!) with some [limitations](limitations.md#config--compileroptions). In addition we add some Lua related options: @@ -74,14 +74,3 @@ tstl --luaTarget 5.1 --strict true script.ts ```bash tstl --luaTarget 5.1 --strict true script1.ts someDir/script2.ts ``` - -## Further Reading - -It's recommended to read the following articles, for more information on advanced usage and limitations of tstl. - -- [Differences between Lua and JS](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Differences-between-Lua-and-Javascript) -- [Writing Declarations](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Writing-Declarations) -- [Compiler Directives](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives) -- [Functions and the self Parameter](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Functions-and-the-self-Parameter) -- [Supported Lua Versions](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Supported-Lua-Versions) -- [Limitations](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Limitations) diff --git a/docs/writing-declarations.md b/docs/writing-declarations.md index 8c5a9f33..1522de63 100644 --- a/docs/writing-declarations.md +++ b/docs/writing-declarations.md @@ -168,7 +168,7 @@ If you're using an editor that seeks out information about functions, variables, declare function print(...args: any[]); ``` -[Try out what this looks like in an editor]() +

Try out what this looks like in an editor

TypeScript uses TSDoc for its comments. TSDoc allows you to also use markdown in your comments! This means pictures, links, tables, code syntax highlighting and more markdown features are available. These may display differently depending on the editor in use. @@ -204,7 +204,7 @@ let [c, d] = array(); // local c, d = unpack(array()) ``` -See [Compiler Directives wiki page](https://github.com/TypeScriptToLua/TypeScriptToLua/wiki/Compiler-Directives) for more directive info. +See [Compiler Annotations](compiler-annotations.md) page for more directive info. ## Environmental Declarations From 53c1443fbb04b5eaa7961d1fc730691c851f8e64 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 28 Feb 2020 09:18:20 +0000 Subject: [PATCH 05/23] Add `SideBySide` component --- docs/compiler-annotations.md | 409 +++++++++++++++---- docs/functions-and-the-self-parameter.md | 34 ++ src/components/SideBySide/index.tsx | 17 + src/components/SideBySide/styles.module.scss | 22 + 4 files changed, 397 insertions(+), 85 deletions(-) create mode 100644 src/components/SideBySide/index.tsx create mode 100644 src/components/SideBySide/styles.module.scss diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md index 46e3f983..13f8c117 100644 --- a/docs/compiler-annotations.md +++ b/docs/compiler-annotations.md @@ -2,6 +2,8 @@ title: Compiler Annotations --- +import { SideBySide } from "@site/src/components/SideBySide"; + To improve translation and compatibility to different Lua interfaces, the TypscriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax. ## @compileMembersOnly @@ -12,44 +14,86 @@ This decorator removes an enumeration's name after compilation and only leaves i **Example** + + ```typescript declare enum myEnum { - myEnum_memberA, Translates to - myEnum_memberB, ============> + myEnum_memberA, + myEnum_memberB, } -const myvar = myEnum.myEnum_memberA; local myvar = myEnum.myEnum_memberA + +const myvar = myEnum.myEnum_memberA; +``` + +```lua +local myvar = myEnum.myEnum_memberA ``` + + + + ```typescript /** @compileMembersOnly */ declare enum myEnum { - myEnum_memberA, Translates to - myEnum_memberB, ============> + myEnum_memberA, + myEnum_memberB, } -const myvar = myEnum.myEnum_memberA; local myvar = myEnum_memberA +const myvar = myEnum.myEnum_memberA; ``` +```lua +local myvar = myEnum_memberA +``` + + + **Example 2** + + ```typescript -enum myEnum { myEnum = {} - myEnum_memberA, Translates to myEnum.myEnum_memberA = 0 - myEnum_memberB, ============> myEnum.myEnum_memberB = 1 - myEnum_memberC = "c" myEnum.myEnum_memberC = "c" +enum myEnum { + myEnum_memberA, + myEnum_memberB, + myEnum_memberC = "c", } -const myvar = myEnum.myEnum_memberA; local myvar = myEnum.myEnum_memberA +const myvar = myEnum.myEnum_memberA; +``` + +```lua +myEnum = {} +myEnum.myEnum_memberA = 0 +myEnum.myEnum_memberB = 1 +myEnum.myEnum_memberC = "c" + +local myvar = myEnum.myEnum_memberA ``` + + + + ```typescript /** @compileMembersOnly */ enum myEnum { - myEnum_memberA, Translates to myEnum_memberA = 0 - myEnum_memberB, ============> myEnum_memberB = 1 - myEnum_memberC = "c" myEnum_memberC = "c" + myEnum_memberA, + myEnum_memberB, + myEnum_memberC = "c", } -const myvar = myEnum.myEnum_memberA; local myvar = myEnum_memberA +const myvar = myEnum.myEnum_memberA; +``` + +```lua +myEnum_memberA = 0 +myEnum_memberB = 1 +myEnum_memberC = "c" + +local myvar = myEnum_memberA ``` + + ## @customConstructor **Target elements:** `declare class` @@ -58,17 +102,37 @@ Changes the way new instances of this class are made. Takes exactly one argument **Example** + + ```typescript -declare class MyClass { constructor(x: number); } Translates to -const inst = new MyClass(3); ============> local inst = MyClass.new(true, 3); +declare class MyClass { + constructor(x: number); +} +const inst = new MyClass(3); ``` +```lua +local inst = MyClass.new(true, 3); +``` + + + + + ```typescript /** @customConstructor MyConstructor */ -declare class MyClass { constructor(x: number); } Translates to -const inst = new MyClass(3); ============> local inst = MyConstructor(3); +declare class MyClass { + constructor(x: number); +} +const inst = new MyClass(3); +``` + +```lua +local inst = MyConstructor(3); ``` + + ## @extension **Target elements:** `class` @@ -77,20 +141,38 @@ The Extension decorator marks a class as an extension of an already existing cla **Example** + + ```typescript -class myBaseClass{ myBaseClass = myBaseClass or {} - myFunction(): void {} Translates to myBaseClass.__index = myBaseClass -} ============> ... - function myBaseClass.myFunction(self) end +class myBaseClass { + myFunction(): void {} +} +``` + +```lua +myBaseClass = myBaseClass or {} +myBaseClass.__index = myBaseClass +... +function myBaseClass.myFunction(self) end ``` + + + + ```typescript /** @extension */ -class myBaseClass{ Translates to - myFunction(): void {} ============> function myBaseClass.myFunction(self) end +class myBaseClass { + myFunction(): void {} } ``` +```lua +function myBaseClass.myFunction(self) end +``` + + + ## @forRange **Target elements:** `declare function` @@ -101,15 +183,24 @@ The function should not be a real function and an error will be thrown if it is **Example** + + + ```typescript /** @forRange */ declare function forRange(start: number, limit: number, step?: number): number[]; - Translates to -for (const i of forRange(1, 10)) {} ============> for i = 1, 10 do end -for (const i of forRange(10, 1, -1)) {} for i = 10, 1, -1 do end +for (const i of forRange(1, 10)) {} +for (const i of forRange(10, 1, -1)) {} ``` +```lua +for i = 1, 10 do end +for i = 10, 1, -1 do end +``` + + + ## @luaIterator **Target elements:** `(declare) interface` @@ -118,31 +209,48 @@ Denotes a type is a Lua iterator. When an object of a type with this annotation **Example** + + + ```typescript /** @luaIterator */ interface MyIterable extends Iterable {} declare function myIterator(): MyIterable; - Translates to -for (let s of myIterator()) {} ============> for s in myIterator() do end + +for (let s of myIterator()) {} +``` + +```lua +for s in myIterator() do end ``` + + This can also be combined with [@tupleReturn](#tuplereturn), if the iterator returns multiple values. **Example** + + + ```typescript // Lua's built-in string.gmatch() iterator declare namespace string { - /** @luaIterator @tupleReturn */ - export interface GmatchIterable extends Array {} + /** @luaIterator @tupleReturn */ + interface GmatchIterable extends Array {} - export function gmatch(s: string, pattern: string): GmatchIterable; + function gmatch(s: string, pattern: string): GmatchIterable; } - Translates to -for (const [a, b] of string.gmatch("foo", "(.)(.)")) {} ============> for a, b in string.gmatch("foo", "(.)(.)") do end +for (const [a, b] of string.gmatch("foo", "(.)(.)")) {} +``` + +```lua +for a, b in string.gmatch("foo", "(.)(.)") do end ``` + + ## @luaTable **Target elements:** `type` @@ -175,27 +283,40 @@ The Extension decorator marks a class as an extension of an already existing met **Example** + + ```typescript -class myBaseClass{ myBaseClass = myBaseClass or {} - myFunction(): void {} Translates to myBaseClass.__index = myBaseClass -} ============> ... - function myBaseClass.myFunction(self) end +class myBaseClass { + myFunction(): void {} +} +``` + +```lua +myBaseClass = myBaseClass or {} +myBaseClass.__index = myBaseClass +... +function myBaseClass.myFunction(self) end ``` + + + + ```typescript /** @metaExtension */ class myMetaExtension extends myMetaClass { - myFunction(): void {} + myFunction(): void {} } +``` -Translates to -============> - +```lua local __meta__myMetaClass = debug.getregistry()["myMetaClass"] __meta__myMetaClass.myFunction = function(self) end; ``` + + ## @noResolution **Target elements:** `module` @@ -204,19 +325,35 @@ Prevents tstl from trying to resolve the module path. When importing this module **Example** + + ```typescript -module MyModule { Translates to -} ============> ... -import module from "mymodule"; local module = require("src.mymodule"); +module MyModule {} +import module from "mymodule"; +``` + +```lua +... +local module = require("src.mymodule"); ``` + + + + ```typescript /** @noResolution */ -module MyModule { Translates to -} ============> ... -import module from "mymodule"; local module = require("mymodule"); +module MyModule {} +import module from "mymodule"; ``` +```lua +... +local module = require("mymodule"); +``` + + + ## @noSelf **Target elements:** `declare class`, `(declare) interface` or `declare namespace` @@ -227,40 +364,58 @@ When applied to a class or interface, this only affects the type's declared meth **Example** + + ```typescript declare interface NormalInterface { - normalMethod(s: string): void; + normalMethod(s: string): void; } declare const x: NormalInterface; /** @noSelf **/ declare interface NoSelfInterface { - noSelfMethod(s: string): void; + noSelfMethod(s: string): void; } declare const y: NoSelfInterface; -x.normalMethod("foo"); Translates to x:normalMethod("foo") -y.noSelfMethod("bar"); ============> y.noSelfMethod("bar") +x.normalMethod("foo"); +y.noSelfMethod("bar"); ``` +```lua +x:normalMethod("foo") +y.noSelfMethod("bar") +``` + + + When applied to a namespace, all functions declared within the namespace will treated as if they do not have a `self` parameter. In this case, the effect is recursive, so functions in nested namespaces and types declared as parameters will also be affected. **Example** + + ```typescript declare namespace NormalNS { - export function normalFunc(s: string): string; + export function normalFunc(s: string): string; } /** @noSelf **/ declare namespace NoSelfNS { - export function noSelfFunc(s: string): string; + export function noSelfFunc(s: string): string; } -NormalNS.normalFunc("foo"); Translates to NormalNS:normalFunc("foo") -NoSelfNS.noSelfFunc("bar"); ============> NoSelfNS.noSelfFunc("bar") +NormalNS.normalFunc("foo"); +NoSelfNS.noSelfFunc("bar"); ``` +```lua +NormalNS:normalFunc("foo") +NoSelfNS.noSelfFunc("bar") +``` + + + For more information about how the `self` parameter is handled, see [Functions and the `self` Parameter](functions-and-the-self-parameter.md) ## @noSelfInFile @@ -281,19 +436,36 @@ This decorator marks a namespace as a phantom namespace. This means all members **Example** + + ```typescript -namespace myNameSpace { Translates to myNameSpace = {} - function myFunction(): void {} ============> function myNameSpace.myFunction() end +namespace myNameSpace { + function myFunction(): void {} } ``` +```lua +myNameSpace = {} +function myNameSpace.myFunction() end +``` + + + + + ```typescript /** !phantom */ -namespace myNameSpace { Translates to - function myFunction(): void {} ============> function myFunction() end +namespace myNameSpace { + function myFunction(): void {} } ``` +```lua +function myFunction() end +``` + + + ## @pureAbstract **Target elements:** `declare class` @@ -302,21 +474,38 @@ This decorator marks a class declaration as purely abstract. The result is that **Example** + + ```typescript -declare class myAbstractClass { myClass = myClass or myAbstractClass.new() -} Translates to myClass.__index = myClass -class myClass extends myAbstractClass { ============> myClass.__base = myAbstractClass -} function myClass.new(... +declare class myAbstractClass {} +class myClass extends myAbstractClass {} ``` +```lua +myClass = myClass or myAbstractClass.new() +myClass.__index = myClass +myClass.__base = myAbstractClass +function myClass.new(... +``` + + + + + ```typescript /** @pureAbstract */ -declare class myAbstractClass { Translates to myClass = myClass or {} -} ============> myClass.__index = myClass -class myClass extends myAbstractClass { function myClass.new(... -} +declare class myAbstractClass {} +class myClass extends myAbstractClass {} +``` + +```lua +myClass = myClass or {} +myClass.__index = myClass +function myClass.new(... ``` + + ## @tupleReturn **Target elements:** `(declare) function` @@ -325,21 +514,43 @@ This decorator indicates a function returns a lua tuple instead of a table. It i **Example** + + ```typescript -function myFunction(): [number, string] { function myFunction() - return [3, "4"]; Translates to return {3, "4"} -} ============> end -let [a,b] = myFunction(); local a,b = unpack(myFunction()) +function myFunction(): [number, string] { + return [3, "4"]; +} +let [a, b] = myFunction(); ``` +```lua +function myFunction() + return {3, "4"} +end +local a,b = unpack(myFunction()) +``` + + + + + ```typescript /** @tupleReturn */ -function myFunction(): [number, string] { function myFunction() - return [3, "4"]; Translates to return 3, "4" -} ============> end -let [a,b] = myFunction(); local a, b = myFunction() +function myFunction(): [number, string] { + return [3, "4"]; +} +let [a, b] = myFunction(); ``` +```lua +function myFunction() + return 3, "4" +end +local a, b = myFunction() +``` + + + If you wish to use this annotation on function with overloads, it must be applied to each signature that requires it. **Example** @@ -361,29 +572,57 @@ Indicates that an array-like type represents a Lua vararg expression (`...`) and **Example** + + ```typescript -function varargWrapUnpack(...args: string[]) { Translates to - console.log(...args); ============> local args = ({...}) -} print(unpack(args)) +function varargWrapUnpack(...args: string[]) { + console.log(...args); +} +``` + +```lua +local args = ({...}) +print(unpack(args)) +``` + + + + +```typescript /** @vararg */ interface Vararg extends Array {} -function varargForward(...args: Vararg) { Translates to - console.log(...args); ============> print(...) +function varargForward(...args: Vararg) { + console.log(...args); } ``` +```lua +print(...) +``` + + + This can be used to access the file-scope varargs as well. **Example** + + ```typescript -declare const arg: Vararg; Translates to -console.log(...arg); ============> print(...) -const [x, y] = [...arg]; local x, y = ... +declare const arg: Vararg; +console.log(...arg); +const [x, y] = [...arg]; ``` +```lua +print(...) +local x, y = ... +``` + + + To also support tuple-typed rest parameters, you can define the type like this: **Example** diff --git a/docs/functions-and-the-self-parameter.md b/docs/functions-and-the-self-parameter.md index 6e2da822..ed0a4de2 100644 --- a/docs/functions-and-the-self-parameter.md +++ b/docs/functions-and-the-self-parameter.md @@ -2,12 +2,16 @@ title: Functions and the `self` Parameter --- +import { SideBySide } from "@site/src/components/SideBySide"; + # Every Function Has a Context Parameter In JavaScript and TypeScript, almost all functions have access to an implicit `this` parameter. In order to maintain compatibility with this, all Lua functions are generated with an extra initial context parameter. **Example** + + ```typescript function myFunction(arg: unknown) {} myFunction("foo"); @@ -19,6 +23,8 @@ end myFunction(nil, "foo") ``` + + The reason for this is that a method can be assigned to a stand-alone function and vice-versa. **Example** @@ -47,6 +53,8 @@ Note that even declared functions are assumed to have this extra parameter as we **Example** + + ```typescript declare function myLibFunction(arg: unknown): void; myLibFunction("foo"); @@ -56,6 +64,8 @@ myLibFunction("foo"); myLibFunction(nil, "foo") ``` + + # Removing the Context Parameter When dealing with external library functions that don't expect this initial parameter, you will need to inform TypeScriptToLua. This can be done a few different ways. @@ -66,6 +76,8 @@ You can declare any function with `this: void` to prevent generation of this ini **Example** + + ```typescript declare function myLibFunction(this: void, arg: unknown): void; myLibFunction("foo"); @@ -75,10 +87,14 @@ myLibFunction("foo"); myLibFunction("foo") ``` + + This works on methods as well, which can be useful if you have class methods which should be called with a dot `.` instead of a colon `:`. **Example** + + ```typescript declare class MyClass { myMethodWithContext(arg: unknown): void; @@ -95,10 +111,14 @@ c:myMethodWithContext("foo") -- uses colon : c.myMethodWithoutContext("foo") -- uses dot . ``` + + Another common scenario is a library function which takes a lua callback function, which should not have a context parameter. **Example** + + ```typescript type Callback = (this: void, arg: unknown) => void; declare function takesCallback(this: void, callback: Callback): void; @@ -111,12 +131,16 @@ takesCallback(arg => { takesCallback(function(arg) print(arg) end) ``` + + ## `@noSelf` If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](compiler-annotations.md#noself) directive. **Example** + + ```typescript /** @noSelf **/ declare namespace MyNamespace { @@ -129,10 +153,14 @@ MyNamespace.myFunction("foo"); MyNamespace.myFunction("foo") ``` + + You can override `@noSelf` on a per-function basis by specifying a `this` parameter. **Example** + + ```typescript /** @noSelf **/ declare namespace MyNamespace { @@ -145,6 +173,8 @@ MyNamespace.myFunction("foo"); MyNamespace:myFunction("foo") ``` + + ## `@noSelfInFile` If you want to specify that all functions in a file should have no context, you can use [`@noSelfInFile`](compiler-annotations.md#noselfinfile) at the top of the file. @@ -168,6 +198,8 @@ This throws an error because if takesCallback called myCallback, it would do so **Example** + + ```typescript takesCallback(arg => myCallback(arg)); ``` @@ -176,6 +208,8 @@ takesCallback(arg => myCallback(arg)); takesCallback(function(arg) return myCallback(nil, arg) end) ``` + + The reason this works is because TypeScriptToLua infers whether the arrow function should take a context parameter or not based on the type it's being assigned to. ## Overloads diff --git a/src/components/SideBySide/index.tsx b/src/components/SideBySide/index.tsx new file mode 100644 index 00000000..31022be3 --- /dev/null +++ b/src/components/SideBySide/index.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import styles from "./styles.module.scss"; + +export function SideBySide({ children }: { children: React.ReactNode }) { + const count = React.Children.count(children); + if (count !== 2) { + throw new Error(`Invalid SideBySide children count: ${count}`); + } + + const [left, right] = React.Children.toArray(children); + return ( +
+ {left} + {right} +
+ ); +} diff --git a/src/components/SideBySide/styles.module.scss b/src/components/SideBySide/styles.module.scss new file mode 100644 index 00000000..af02f2e9 --- /dev/null +++ b/src/components/SideBySide/styles.module.scss @@ -0,0 +1,22 @@ +.sideBySide { + display: flex; + + @media screen and (max-width: 996px) { + flex-flow: column; + } + + @media screen and (min-width: 997px) { + > :first-child { + padding-right: 5px; + } + + > :last-child { + padding-left: 5px; + } + } + + > div { + flex: 1 1 auto; + overflow: auto; + } +} From 89707bc46456414b3e69a75d79584b592afc699c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 28 Feb 2020 11:45:30 +0000 Subject: [PATCH 06/23] Update some code examples --- docs/api/transformer.md | 36 ++--- docs/compiler-annotations.md | 169 +++++++++++++---------- docs/functions-and-the-self-parameter.md | 40 +++--- docs/writing-declarations.md | 59 +++----- 4 files changed, 146 insertions(+), 158 deletions(-) diff --git a/docs/api/transformer.md b/docs/api/transformer.md index ea054ff0..72377a9f 100644 --- a/docs/api/transformer.md +++ b/docs/api/transformer.md @@ -45,6 +45,7 @@ class CustomTransformer extends tstl.LuaTransformer { This is a list of all public overridable methods in the default TypeScriptToLua tranformer: + ```ts class LuaTransformer { public transformSourceFile(sourceFile: ts.SourceFile): [tstl.Block, Set]; @@ -57,32 +58,15 @@ class LuaTransformer { public transformImportDeclaration(declaration: ts.ImportDeclaration): StatementVisitResult; - public transformClassDeclaration( - declaration: ts.ClassLikeDeclaration, - nameOverride?: tstl.Identifier, - ): StatementVisitResult; - - public transformGetAccessorDeclaration( - declaration: ts.GetAccessorDeclaration, - className: tstl.Identifier, - ): StatementVisitResult; - - public transformSetAccessorDeclaration( - declaration: ts.GetAccessorDeclaration, - className: tstl.Identifier, - ): StatementVisitResult; - - public transformMethodDeclaration( - declaration: ts.MethodDeclaration, - className: tstl.Identifier, - noPrototype: boolean, - ): StatementVisitResult; - - public transformBindingPattern( - pattern: ts.BindingPattern, - table: tstl.Identifier, - propertyStack: ts.PropertyName, - ): StatementVisitResult; + public transformClassDeclaration(declaration: ts.ClassLikeDeclaration, nameOverride?: tstl.Identifier): StatementVisitResult; + + public transformGetAccessorDeclaration(declaration: ts.GetAccessorDeclaration, className: tstl.Identifier): StatementVisitResult; + + public transformSetAccessorDeclaration(declaration: ts.GetAccessorDeclaration, className: tstl.Identifier): StatementVisitResult; + + public transformMethodDeclaration(declaration: ts.MethodDeclaration, className: tstl.Identifier, noPrototype: boolean): StatementVisitResult; + + public transformBindingPattern(pattern: ts.BindingPattern, table: tstl.Identifier,propertyStack: ts.PropertyName): StatementVisitResult; public transformModuleDeclaration(declaration: ts.ModuleDeclaration): StatementVisitResult; diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md index 13f8c117..5df3f418 100644 --- a/docs/compiler-annotations.md +++ b/docs/compiler-annotations.md @@ -17,16 +17,16 @@ This decorator removes an enumeration's name after compilation and only leaves i ```typescript -declare enum myEnum { - myEnum_memberA, - myEnum_memberB, +declare enum MyEnum { + MY_ENUM_MEMBER_A, + MY_ENUM_MEMBER_B, } -const myvar = myEnum.myEnum_memberA; +print(MyEnum.MY_ENUM_MEMBER_A); ``` ```lua -local myvar = myEnum.myEnum_memberA +print(MyEnum.MY_ENUM_MEMBER_A) ``` @@ -35,15 +35,16 @@ local myvar = myEnum.myEnum_memberA ```typescript /** @compileMembersOnly */ -declare enum myEnum { - myEnum_memberA, - myEnum_memberB, +declare enum MyEnum { + MY_ENUM_MEMBER_A, + MY_ENUM_MEMBER_B, } -const myvar = myEnum.myEnum_memberA; + +print(MyEnum.MY_ENUM_MEMBER_A); ``` ```lua -local myvar = myEnum_memberA +print(MY_ENUM_MEMBER_A) ``` @@ -53,21 +54,22 @@ local myvar = myEnum_memberA ```typescript -enum myEnum { - myEnum_memberA, - myEnum_memberB, - myEnum_memberC = "c", +enum MyEnum { + MY_ENUM_MEMBER_A, + MY_ENUM_MEMBER_B, + MY_ENUM_MEMBER_C = "c", } -const myvar = myEnum.myEnum_memberA; + +print(MyEnum.MY_ENUM_MEMBER_A); ``` ```lua -myEnum = {} -myEnum.myEnum_memberA = 0 -myEnum.myEnum_memberB = 1 -myEnum.myEnum_memberC = "c" +MyEnum = {} +MyEnum.MY_ENUM_MEMBER_A = 0 +MyEnum.MY_ENUM_MEMBER_B = 1 +MyEnum.MY_ENUM_MEMBER_C = "c" -local myvar = myEnum.myEnum_memberA +print(MyEnum.MY_ENUM_MEMBER_A) ``` @@ -76,20 +78,21 @@ local myvar = myEnum.myEnum_memberA ```typescript /** @compileMembersOnly */ -enum myEnum { - myEnum_memberA, - myEnum_memberB, - myEnum_memberC = "c", +enum MyEnum { + MY_ENUM_MEMBER_A, + MY_ENUM_MEMBER_B, + MY_ENUM_MEMBER_C = "c", } -const myvar = myEnum.myEnum_memberA; + +print(MyEnum.MY_ENUM_MEMBER_A); ``` ```lua -myEnum_memberA = 0 -myEnum_memberB = 1 -myEnum_memberC = "c" +MY_ENUM_MEMBER_A = 0 +MY_ENUM_MEMBER_B = 1 +MY_ENUM_MEMBER_C = "c" -local myvar = myEnum_memberA +print(MY_ENUM_MEMBER_A) ``` @@ -112,7 +115,7 @@ const inst = new MyClass(3); ``` ```lua -local inst = MyClass.new(true, 3); +local inst = __TS__New(MyClass, 3) ``` @@ -128,7 +131,7 @@ const inst = new MyClass(3); ``` ```lua -local inst = MyConstructor(3); +local inst = MyConstructor(3) ``` @@ -144,16 +147,15 @@ The Extension decorator marks a class as an extension of an already existing cla ```typescript -class myBaseClass { +class MyBaseClass { myFunction(): void {} } ``` ```lua -myBaseClass = myBaseClass or {} -myBaseClass.__index = myBaseClass +MyBaseClass = __TS__Class() ... -function myBaseClass.myFunction(self) end +function MyBaseClass.prototype.myFunction(self) end ``` @@ -162,13 +164,13 @@ function myBaseClass.myFunction(self) end ```typescript /** @extension */ -class myBaseClass { +class MyBaseClass { myFunction(): void {} } ``` ```lua -function myBaseClass.myFunction(self) end +function MyBaseClass.myFunction(self) end ``` @@ -214,10 +216,10 @@ Denotes a type is a Lua iterator. When an object of a type with this annotation ```typescript /** @luaIterator */ -interface MyIterable extends Iterable {} -declare function myIterator(): MyIterable; +type LuaIterable = Iterable; -for (let s of myIterator()) {} +declare function myIterator(): LuaIterator; +for (const s of myIterator()) {} ``` ```lua @@ -234,12 +236,11 @@ This can also be combined with [@tupleReturn](#tuplereturn), if the iterator ret ```typescript -// Lua's built-in string.gmatch() iterator -declare namespace string { - /** @luaIterator @tupleReturn */ - interface GmatchIterable extends Array {} +/** @luaIterator @tupleReturn */ +type LuaTupleIterable = Iterable; - function gmatch(s: string, pattern: string): GmatchIterable; +declare namespace string { + function gmatch(s: string, pattern: string): LuaTupleIterable; } for (const [a, b] of string.gmatch("foo", "(.)(.)")) {} @@ -286,16 +287,15 @@ The Extension decorator marks a class as an extension of an already existing met ```typescript -class myBaseClass { +class MyBaseClass { myFunction(): void {} } ``` ```lua -myBaseClass = myBaseClass or {} -myBaseClass.__index = myBaseClass +MyBaseClass = __TS__Class() ... -function myBaseClass.myFunction(self) end +function MyBaseClass.prototype.myFunction(self) end ``` @@ -304,14 +304,14 @@ function myBaseClass.myFunction(self) end ```typescript /** @metaExtension */ -class myMetaExtension extends myMetaClass { +class MyMetaExtension extends MyMetaClass { myFunction(): void {} } ``` ```lua -local __meta__myMetaClass = debug.getregistry()["myMetaClass"] -__meta__myMetaClass.myFunction = function(self) +local __meta__MyMetaClass = debug.getregistry().MyMetaClass +__meta__MyMetaClass.myFunction = function(self) end; ``` @@ -328,7 +328,7 @@ Prevents tstl from trying to resolve the module path. When importing this module ```typescript -module MyModule {} +declare module "mymodule" {} import module from "mymodule"; ``` @@ -343,7 +343,7 @@ local module = require("src.mymodule"); ```typescript /** @noResolution */ -module MyModule {} +declare module "mymodule" {} import module from "mymodule"; ``` @@ -397,12 +397,12 @@ When applied to a namespace, all functions declared within the namespace will tr ```typescript declare namespace NormalNS { - export function normalFunc(s: string): string; + function normalFunc(s: string): string; } /** @noSelf **/ declare namespace NoSelfNS { - export function noSelfFunc(s: string): string; + function noSelfFunc(s: string): string; } NormalNS.normalFunc("foo"); @@ -454,7 +454,7 @@ function myNameSpace.myFunction() end ```typescript -/** !phantom */ +/** @phantom */ namespace myNameSpace { function myFunction(): void {} } @@ -477,15 +477,16 @@ This decorator marks a class declaration as purely abstract. The result is that ```typescript -declare class myAbstractClass {} -class myClass extends myAbstractClass {} +declare class MyAbstractClass {} +class MyClass extends MyAbstractClass {} ``` ```lua -myClass = myClass or myAbstractClass.new() -myClass.__index = myClass -myClass.__base = myAbstractClass -function myClass.new(... +MyClass = __TS__Class() +MyClass.__base = MyAbstractClass +MyClass.____super = MyAbstractClass +setmetatable(MyClass, MyClass.____super) +setmetatable(MyClass.prototype, MyClass.____super.prototype) ``` @@ -494,14 +495,12 @@ function myClass.new(... ```typescript /** @pureAbstract */ -declare class myAbstractClass {} -class myClass extends myAbstractClass {} +declare class MyAbstractClass {} +class MyClass extends MyAbstractClass {} ``` ```lua -myClass = myClass or {} -myClass.__index = myClass -function myClass.new(... +MyClass = __TS__Class() ``` @@ -520,7 +519,7 @@ This decorator indicates a function returns a lua tuple instead of a table. It i function myFunction(): [number, string] { return [3, "4"]; } -let [a, b] = myFunction(); +const [a, b] = myFunction(); ``` ```lua @@ -539,7 +538,7 @@ local a,b = unpack(myFunction()) function myFunction(): [number, string] { return [3, "4"]; } -let [a, b] = myFunction(); +const [a, b] = myFunction(); ``` ```lua @@ -581,8 +580,10 @@ function varargWrapUnpack(...args: string[]) { ``` ```lua -local args = ({...}) -print(unpack(args)) +function varargWrapUnpack(self, ...) + local args = ({...}) + print(unpack(args)) +end ``` @@ -599,7 +600,9 @@ function varargForward(...args: Vararg) { ``` ```lua -print(...) +function varargForward(self, ...) + print(...)) +end ``` @@ -629,22 +632,36 @@ To also support tuple-typed rest parameters, you can define the type like this: ```typescript /** @vararg */ -type Vararg = A & { __luaVararg?: never }; +type Vararg = T & { __luaVararg?: never }; function varargForward(...args: Vararg<[string, number]>) {} ``` **_Warning_** -TypeScriptToLua does not check that the vararg expression is valid in the context it is used. If the array is used in a spread operation in an invalid context (such as a nested function), a Lua compiler error will occur. +TypeScriptToLua does not check that the vararg expression is valid in the context it is used. If the array is used in a spread operation in an invalid context (such as a nested function), a deoptimization will occur. **Example** + + ```typescript function outerFunction(...args: Vararg) { function innerFunction() { - console.log(...args); // cannot use '...' outside a vararg function + console.log(...args); } innerFunction(); } ``` + +```lua +function outerFunction(self, ...) + local args = {...} + local function innerFunction(self) + print(unpack(args)) + end + innerFunction(_G) +end +``` + + diff --git a/docs/functions-and-the-self-parameter.md b/docs/functions-and-the-self-parameter.md index ed0a4de2..32bce6cc 100644 --- a/docs/functions-and-the-self-parameter.md +++ b/docs/functions-and-the-self-parameter.md @@ -13,7 +13,7 @@ In JavaScript and TypeScript, almost all functions have access to an implicit `t ```typescript -function myFunction(arg: unknown) {} +function myFunction(arg: string) {} myFunction("foo"); ``` @@ -31,13 +31,15 @@ The reason for this is that a method can be assigned to a stand-alone function a ```typescript class MyClass { - myMethod(arg: unknown) { + myMethod(arg: string) { console.log("myMethod", arg); } } -var myFunction = function(arg: unknown) { + +let myFunction = function(arg: string) { console.log("myFunction", arg); }; + const c = new MyClass(); c.myMethod = myFunction; @@ -56,7 +58,7 @@ Note that even declared functions are assumed to have this extra parameter as we ```typescript -declare function myLibFunction(arg: unknown): void; +declare function myLibFunction(arg: string): void; myLibFunction("foo"); ``` @@ -79,7 +81,7 @@ You can declare any function with `this: void` to prevent generation of this ini ```typescript -declare function myLibFunction(this: void, arg: unknown): void; +declare function myLibFunction(this: void, arg: string): void; myLibFunction("foo"); ``` @@ -97,18 +99,18 @@ This works on methods as well, which can be useful if you have class methods whi ```typescript declare class MyClass { - myMethodWithContext(arg: unknown): void; - myMethodWithoutContext(this: void, arg: unknown): void; + withContext(arg: string): void; + withoutContext(this: void, arg: string): void; } const c = new MyClass(); -c.myMethodWithContext("foo"); -c.myMethodWithoutContext("foo"); +c.withContext("foo"); +c.withoutContext("foo"); ``` ```lua -local c = MyClass.new() -c:myMethodWithContext("foo") -- uses colon : -c.myMethodWithoutContext("foo") -- uses dot . +local c = __TS__New(MyClass) +c:withContext("foo") -- uses colon : +c.withoutContext("foo") -- uses dot . ``` @@ -119,9 +121,13 @@ Another common scenario is a library function which takes a lua callback functio + ```typescript -type Callback = (this: void, arg: unknown) => void; -declare function takesCallback(this: void, callback: Callback): void; +declare function takesCallback( + this: void, + callback: (this: void, arg: string) => void, +): void; + takesCallback(arg => { console.log(arg); }); @@ -144,7 +150,7 @@ If you wish to specify that all functions in a class, interface or namespace sho ```typescript /** @noSelf **/ declare namespace MyNamespace { - export function myFunction(arg: unknown): void; + function myFunction(arg: string): void; } MyNamespace.myFunction("foo"); ``` @@ -164,7 +170,7 @@ You can override `@noSelf` on a per-function basis by specifying a `this` parame ```typescript /** @noSelf **/ declare namespace MyNamespace { - export function myFunction(this: any, arg: unknown): void {} + function myFunction(this: any, arg: string): void; } MyNamespace.myFunction("foo"); ``` @@ -194,7 +200,7 @@ function myCallback(arg: string) {} takesCallback(myCallback); // Error: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. ``` -This throws an error because if takesCallback called myCallback, it would do so without passing an initial context parameter. This can be easily fixed simply by wrapping the call in an arrow function. +This throws an error because if `takesCallback` called `myCallback`, it would do so without passing an initial context parameter. This can be easily fixed simply by wrapping the call in an arrow function. **Example** diff --git a/docs/writing-declarations.md b/docs/writing-declarations.md index 1522de63..f2f3cad0 100644 --- a/docs/writing-declarations.md +++ b/docs/writing-declarations.md @@ -26,8 +26,7 @@ The _declare_ keyword is used to say that the following declaration defines some This is useful for defining Lua's environment. -```ts -// _G.d.ts +```ts title=_G.d.ts // Uses some declarations from // https://www.lua.org/manual/5.1/manual.html @@ -47,8 +46,7 @@ declare const _VERSION: number; declare function print(...args: any[]): void; ``` -```ts -// main.ts +```ts title=main.ts print(_VERSION); // Editor and transpiler know what print and _VERSION are ``` @@ -62,20 +60,17 @@ This also includes ambient interfaces, types, modules and other items that don't If a file named _lib.lua_ exists and returns a table with an _x_ field, you can write _lib.d.t.s_ as follows to tell TypeScript that _lib_ exists and what it provides. -```ts -// lib.d.ts +```ts title=lib.d.ts export let x: number; ``` -```ts -// main.ts +```ts title=main.ts import { x } from "./lib"; ``` If a namespace contains certain functions, `export` tells TypeScript that those functions can be accessed within the namespace. -```ts -// .d.ts +```ts title=.d.ts declare namespace table { /** * @noSelf @@ -84,15 +79,13 @@ declare namespace table { } ``` -```ts -// .ts +```ts title=main.ts table.insert({}, 1); ``` If a globally available module exists within the Lua environment. You can define what the module provides. -```ts -// .d.ts +```ts title=.d.ts declare module "utf8" { /** * @noSelf @@ -158,12 +151,10 @@ declare namespace table { If you're using an editor that seeks out information about functions, variables, etc. It will likely find the file where what it is analyzing is defined and check out the comment above it. -```ts -// print.d.ts - +```ts title=print.d.ts /** * When hovering over print, this description will be shown - * @param {any[]} args Stuff to print + * @param args Stuff to print */ declare function print(...args: any[]); ``` @@ -240,15 +231,14 @@ Some examples of declaration merging have been shown in the above examples. Some tables can use `__call` to make themselves callable. Busted (the Lua testing suite) does this to `assert`. -```ts -// .d.ts +```ts title=.d.ts declare namespace assert { export function isEqual(): void; } declare function assert(value: any, errorDescription?: string): void; ``` -```ts +```ts title=main.ts assert.isEqual(); assert(); ``` @@ -257,8 +247,7 @@ assert(); ### Interfaces -```ts -// .d.ts +```ts title=.d.ts interface Image { /** @tupleReturn */ getDimensions(): [number, number]; @@ -271,8 +260,7 @@ interface Image { } ``` -```ts -// usage.ts +```ts title=main.ts declare let image: Image; let [w, h] = image.getDimensions(); // local w, h = image:getDimensions() let o = image.getFlags(); @@ -280,8 +268,7 @@ let o = image.getFlags(); ### Namespaces -```ts -// .d.ts +```ts title=.d.ts declare namespace love { export let update: (delta: number) => void; /** @tupleReturn */ @@ -303,8 +290,7 @@ declare namespace string { } ``` -```ts -// usage.ts +```ts title=main.ts let [a, b, c, d] = love.getVersion(); let p = love.graphics.newImage("file.png"); ``` @@ -313,15 +299,13 @@ let p = love.graphics.newImage("file.png"); You'd only declare these if there were TypeScriptToLua compatible classes within the existing Lua code. It is definitely not recommended to define classes in ambient contexts for TypeScriptToLua, use interfaces instead. -```ts -// .d.ts +```ts title=.d.ts declare class X { tuple(); } ``` -```ts -// usage.ts +```ts title=main.ts let p = new X(); p.tuple(); ``` @@ -332,8 +316,7 @@ You may have to use the `@noResolution` directive to tell TypeScriptToLua to not Module declarations need to be kept in _.d.ts_ files. -```ts -// .d.ts +```ts title=.d.ts /** @noSelf */ declare module "image-size" { export function getimagewidth(filename: string): number; @@ -356,8 +339,7 @@ declare module "number-of-the-day" { declare module "custommodule"; ``` -```ts -// .ts +```ts title=main.ts import { getimagewidth, getimageheight } from "image-size"; import * as x from "contains_a_number"; import * as custommodule from "custommodule"; @@ -367,8 +349,7 @@ import * as custommodule from "custommodule"; Unions can be used to tell TypeScript that a given type could be one of many other types. TypeScript can then pick up hints in the code to figure out what that type is at a given statement. -```ts -// .ts +```ts title=main.ts declare interface PingResponse { type: "ping"; timeTaken: number; From 5ccdb29c8ed18433d7838c226e556f2000626624 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 29 Feb 2020 01:56:53 +0000 Subject: [PATCH 07/23] Add `Configuration` page --- docs/configuration.md | 43 +++++++++++++++++++++++++++++++++++++++++ docs/getting-started.md | 18 +---------------- docs/limitations.md | 21 -------------------- sidebars.json | 1 + 4 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 docs/configuration.md diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 00000000..cdc98d54 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,43 @@ +--- +title: Configuration +--- + +TypeScriptToLua uses the same configuration file as the vanilla TypeScript compiler, loading it from the `tsconfig.json` file using the same rules as `tsc`. + +# Custom options + +To customize transpilation behavior we add a new group of options to the `tsconfig.json` file. All of these options should be placed in a `tstl` object. + +```json title=tsconfig.json +{ + "tstl": { + // custom options + } +} +``` + +| Option | Values | Description | +| -------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `luaTarget` | `"JIT"`, `"5.3"`, `"5.2"`, `"5.1"` (default: `"JIT"`) | Specifies the Lua version you want to generate code for. | +| `noImplicitSelf` | `true`, `false` (default: `false`) | If true, treats all project files as if they were prefixed with
`/** @noSelfInFile **/`. | +| `noHeader` | `true`, `false` (default: `false`) | Set this to true if you don't want to include our header in the output. | +| `luaLibImport` | `"inline"`, `"require"`, `"always"`, `"none"` (default: `"require"`) | We polyfill certain JavaScript features with Lua functions, this option specifies how these functions are imported into the Lua output. | +| `sourceMapTraceback` | `true`, `false` (default: `false`) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | +| `luaBundle` | File path (relative to the `tsconfig.json`) | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | +| `luaBundleEntry` | File path (relative to the `tsconfig.json`) | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | + +# Standard options + +Most of the standard [TypeScript options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) work without any changes. Notable unsupported options are: + +- `composite`, `build` +- `incremental` +- `emitDecoratorMetadata` +- `esModuleInterop` +- `jsx` + +Some options do not apply to TypeScriptToLua and are ignored: + +- `outFile` - use `luaBundle` instead. +- `importHelpers`, `noEmitHelpers` - use `luaLibImport` instead. +- `target`, `module` - it's only effect is limiting some features, so prefer to set it to `esnext`. If TypeScript requires you to specify different `module` type because you want to bundle your declarations with `outFile` and `declarations`, consider using [API Extractor](https://api-extractor.com/) instead. diff --git a/docs/getting-started.md b/docs/getting-started.md index 0d0e8999..667f7494 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -33,23 +33,7 @@ Example: } ``` -Because we use the same configuration system `tsc` uses, you can use most of the [options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) available for vanilla TS (including source maps!) with some [limitations](limitations.md#config--compileroptions). - -In addition we add some Lua related options: - -### TSTL specific options - -| Option | Values | Description | -| -------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `luaTarget` | `"JIT"`, `"5.3"`, `"5.2"`, `"5.1"` (default: `"JIT"`) | Specifies the Lua version you wan to generate code for. | -| `noImplicitSelf` | `true`, `false` (default: `false`) | If true, treats all project files as if they were prefixed with
`/** @noSelfInFile **/`. | -| `noHeader` | `true`, `false` (default: `false`) | Set this to true if you don't want to include our header in the output. | -| `luaLibImport` | `"inline"`, `"require"`, `"always"`, `"none"` (default: `"require"`) | We polyfill certain JavaScript features with Lua functions, this option specifies how these functions are imported into the Lua output. | -| `sourceMapTraceback` | `true`, `false` (default: `false`) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | -| `luaBundle` | File path (relative to the `tsconfig.json`) | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | -| `luaBundleEntry` | File path (relative to the `tsconfig.json`) | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | - -**IMPORTANT** These options need to be set in the `tstl` object of your `tsconfig.json`, do not set them inside `compilerOptions` (see example above). +Check out [Configuration](configuration.md) page for more information. ## Building your project diff --git a/docs/limitations.md b/docs/limitations.md index 735d88b4..b98675a9 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -4,27 +4,6 @@ title: Limitations There are certain features of Typescript, that are either not allowed or only partially supported. The following is a list of limitations. -## Config / CompilerOptions - -Important Supported options are: - -- rootDir -- outDir -- project -- (baseUrl) -- skipLibCheck -- strict -- noImplicitAny -- noImplicitThis - -For options not listed here, as a rule of thumb: **all options that configure type checking will work.** - -All other options are most likely not supported and will have no effect. - -**baseUrl** - -Does only work if your base url points to a directory inside your rootDir. Avoid using baseUrl if possible. - ## String functions Not all string functions are supported. The supported functions are: diff --git a/sidebars.json b/sidebars.json index 3b11212b..cca3ba02 100644 --- a/sidebars.json +++ b/sidebars.json @@ -1,6 +1,7 @@ { "docs": [ "getting-started", + "configuration", "differences-between-lua-and-javascript", "writing-declarations", "compiler-annotations", From f7328fb56e7aed0337ee9887d0e5b2e641bbd2d4 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 18:11:48 +0000 Subject: [PATCH 08/23] Add `Editor Support` page --- docs/editor-support.md | 46 +++++++++++++++++++ sidebars.json | 1 + static/images/editor-support-diagnostics.png | Bin 0 -> 37018 bytes 3 files changed, 47 insertions(+) create mode 100644 docs/editor-support.md create mode 100644 static/images/editor-support-diagnostics.png diff --git a/docs/editor-support.md b/docs/editor-support.md new file mode 100644 index 00000000..9ac7a781 --- /dev/null +++ b/docs/editor-support.md @@ -0,0 +1,46 @@ +--- +title: Editor Support +--- + +To have basic support for TypeScriptToLua it is enough to [configure your editor for TypeScript support](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Editor-Support). + +## Language Service Plugin + +Sometimes TypeScriptToLua has to report it's own errors during the compilation. To have the same errors displayed in your editor, you can use a [language service plugin](https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin). + +![](/images/editor-support-diagnostics.png) + +To use it either get a [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=ark120202.vscode-typescript-to-lua) or [add it to your project](https://github.com/TypeScriptToLua/typescript-tstl-plugin#installation). + +## Build Tasks + +Most of advanced code editors can build your project with [npm scripts](https://docs.npmjs.com/misc/scripts). + +```json title=package.json +{ + "scripts": { + "build": "tstl", + "dev": "tstl --watch" + } +} +``` + +### Visual Studio Code + +VSCode supports running npm scripts using [tasks](https://code.visualstudio.com/docs/editor/tasks). To define a task, create a `.vscode/tasks.json` file or press `F1` and run `Tasks: Configure Task` command. Example configuration: + +```json title=tasks.json +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "dev", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { "reveal": "never" }, + "group": { "kind": "build", "isDefault": true } + } + ] +} +``` diff --git a/sidebars.json b/sidebars.json index cca3ba02..fd09da2f 100644 --- a/sidebars.json +++ b/sidebars.json @@ -8,6 +8,7 @@ "functions-and-the-self-parameter", "supported-lua-versions", "limitations", + "editor-support", { "type": "category", "label": "API", diff --git a/static/images/editor-support-diagnostics.png b/static/images/editor-support-diagnostics.png new file mode 100644 index 0000000000000000000000000000000000000000..167e64c4d89fd3173b93a9280a405a88e1508fdd GIT binary patch literal 37018 zcmcG#2UJsA+bxWupa^0^M4BFtq9P!@21Ha;5LA>70TD2igqlD?Q4lyvvr$3`0!oqI z2_Zp1L4nW$B#?rL)R0g^3xpf+obP?#JHGq>{~hE17=yviX6?22de*b&Gv{0@FYcNc z3JIJP;NjsBx^?r~eIA~@P#&ILTKo98XUZi7=D2@$`QA6Y%2V7aF~$9`$5sE1J`Yb> z{DBR}z1+|Hy>43h^6-caZ2#^$(bLA@;d!NS>ze+90DHzJJLA~HrddQMuqoQ`*ri|A zce9S~`@=%y?v?8vug%L2i|jpotSljVpTKTw($hcw*gf=@+{LT1Klfg{^091e3^_3v z+}VK~WY>4jOr&8=GXMw_3K{HjCnVd=xhTf^wb)@Xs|1m?wUN!MvgX%re6jXskGF|z zbq+gq4l3yZ zFN=1jT{eW4C@!V|oOkWq;N-qW`31(Zns1h^*Q9FKj3z!l2WTZ+zDotsjTZ({0Zu2vW|%t_n^F!p<}CwUve zRr{cHB10RWe`-&?!$APym%`DHpajjno{FVD>B*|p1l7>;#+khpTZ*B`a&rkc>2ujX zn)ur{o&WBF=XVzm-deHr4IxxtHvSI?a^W~bo2a`LVc;U7$L{))67N}xX!0;2937HO z85vdODE+=z=&3`E^&Sih)WPRlc!o!(2J*VW_-s5wVfi3KPLJL26F1w@mw>>(FNlKY zt`v5bMJw28JsJpTe{kjRNjh{>bY>aozy}DR7zeVlkmv$HU%eCYy0Y__cn;{>l`S3f z;i()DN5-XBRun%IcV#KANy)`S16-Vr{k+7WBU=Tc@*2wp==1Pa9|5rJCD&iizjM~? zl7?+4kw4*2I2K*0x7t@yPLFQtH8lKeT>M(@9gD4^`zR^SUG!b)<;QdMJQXKGH61w1 zHG0tX3EpwyqD@C;bt*pQ87k1XSmQ={krP+8mOg|mB7cnJ7`{?Rj%)S4KBraWrG6q z=^G3Ix>x&?Eu~*|4412^5M5#*!RHP!kbZ%*$-q%RI)dK;tCKAz?8ohH=&EY!t?9X_ zeUQ45`Q1aqEy?#w;ndz{MO(GaQe@qkK0a8w`dg{tM=6mkKLr|mAC&5U6C?OL_fSda zN!Ujmn27>;(WQfOHV3#BCsh}0m>M!#ldPC3Ku}|-T09?T5Ma;5K7u3{O|2@kd}+@P zbi5jyh;Bz=LoidKC}5fcY@9BNm-KAK-y~NoFIbI#GE8HU;hzQfbx9{Xrx0Q90vEH8 zb3X)pzh04H@>^#J4IcRF%Wk}rZz>R2A7bk5oQS;-xfKAX4ejG85nxO+;Y+FHs??nX!`WNU1qqM0z*NexUU2L>8SN`{{a@pF zrnAI+-@4x9m$~*Ze&`-CqP;`uj5=v*7@7E*S$WVvfb5twzj2(Pt^>%I5`@ZPIxevt zAY~KGGSMLCUV*)jZ}n*zr}ns*JZ4$B#~&lIuaQ+kkue{pglNTHI`Bl%$%fCis#~SV zN>vrp_@$rtQzXx632jZgC#HXA{K9EyskXyC*GZKSlNd!0BV!o+J4yv_5acY-B6(R^ zOV??9D zt&efxb`u|6CusIR6pj)%(*f5MBmyYFb7xfdL!>EpxYw&gd}i5SH``%(;H^tXxJ}2y?A%mb5~9tbm(a-ke7s^cAs1gpz~D<7 zpn{k~BR_GM!l_#0OXYwX1ggQGk#OD?6DrkFtN8J>&c9XGt9^en4-faknQKo{bg>HAY2 zJ7VO|W^%5S+C-?1|3!rj zbmexG>FI#aA=+D22X#16PUC%Dna;VAwBIlJd(oF-GsUV-o;<(kw`^W$Z#1@5ag?4h z_d%WW(2tg7T&54Qt?ck2RdZ|J_G$xW+#p~&T1|wh(Lf!~0lia_8&MoS=w6c{(egP$uqRuGSrvR_SVJv{3I5y-U+c3 z-ivXvAG|N<5xTi5uiR~R?rw!ck|MOi{H-FC{&NhZD)Dl}GBq$Q^LGKg!pnW=MBGN9 zR0|1zjwB@R#z%qmcFZJi#aRd^@1ps{NeMtLLW16(DW$p}lw&2mj^Kx?+m~u*7cHK( z>kmmJuvdO(`D4+UgQ}Y?5XB;TCrr%e0~W^IO;BgvJ!5sUqEVuP_2zp!Aud_<4({*r zuqLWZVmR?#nifoN=|axP9W7Eh0hanHYj&CW6xlN`R*{5=^vDY=l|9grXE9TkIJFDZ+${?_`!7@rr?B?Z|L``nWs<88FV!B_966{xVy7F)3dr z3D5b^e)j;CtX(6q(*E>a^EnmGk+~#Kmz0vL zPR=Ib0ah~r4aV!UX9+`h)C|n+X7**vrzolm%~{Tr3$5#gp6QowX2wyk{=HQdGLO5axs{;Pru{Rd=HaG>(TD>+yV?~6J^64 z)Wa3ywaGUckpLG|?%&UZjh3D{zU13v#o8^Q40|y}Y&i3jp{=#LwsIo&HC_Bh=In0e zPZ}Z25|_3tk@Urbwhob(W;X^`XINy+p5|wl!l>@F%Wu^r)QO!y_mpB3c-|Tzp6WS_U z?7rCumL}BATN(zy8YQf^>z$A913TIZ4*Zx zo^Q9a94r*X>FN`;$itlc8u$N0;bZZ2{z>wOlM)r3`i~P7TufWvd?ufvzB-g}XK=23 zt?C359@NlrDF|(@U|QhY8Bm-Lgpb!hXOLRVL&g65hb_EHc-{z(;vDFB&mW(9p~PNA z+4CEQo`o!55F)62QGOm)FzZK9**dB`l@M(*I}F4Nva-_cMqEdcf?waG(6SxhuD6E! z*W&l*yEcUMQ0kebAe`)*C^jDPQek<1a{XS81GiT!%@1IVlEQpsIiTOf=B2xa$JTbHL74{Ue(A< z#*aTw-)cHtk@i_?r-!BcdobS=bBW+4MW1M=C3}rW10VLE8tE6C`TY`HaQ~Y5bMAzw zt|e2!>;_@zjAVu>#BS%(Zp`7CKZd~&`ebHc=L~Wd$?VsB%rgS4&Z$a2&sQL-I}DQg zPny|zSpd`muxAQ(&1+!&ccAZz!i~~sN@@-L0piu_e2OI&YD1I)&5FR?WE2M4hLTms zZ?+y1{rE>13^uvf;*;YsDRp+}lZ>q|Ip0y+l7st$G`(W(?wf^M?mqtbWz zQql1pewt-%woJLVlri!!2fNEc`D~y^mHr^d!#MM9t#=i}jhax);&8WqpgrMvgE2lWWO1gn;%7$|{ z;_%G5@&6cFjde{(?_oZUTaIo(NrUm7UwWh}fVe_B8FD-@$ME(;nLU`;>?ke~xz5%a zg5nWb+2sGGk3E>Gp2|P_|_i%#0B;o0z1Y< zwPtT$#?0Ov2>SCW3DVy7i-oOp$@UAiMWt!4kDg;s!0bXG-r_ImnQK4*7S-i};`V%d4HCAaBft&0wy^Omu0dpD(cX7@QmMjc-5Iq;CmFEu=u>lb zk4-A?EGBtBHj_a)tW3w|_*2&&iDzK5yVazIpR6YcRi!EVHy?>D)8<0jX5x=US1zQr z%t(ro;}h0oJ47~RCENjd!voZ>uY7yO$EHgyKL9sHTE(Bt1jP)d87Ws>zFmB{&F z38fm+Q+mf~<#|F<$J|Ha+QbE)t7WCvP7m_vl>iCX6J&0nnwEG2P z$a{wx=S;D+CB=%l(NKt*?RwPUe9cxyAYpwXG`U)ugAIJ#3RW)sip&lFC$IL0Q=jDw zr;WNq3i$>@?NppxO;$Q=EYN4X4VvY~iTs&%mM>e5OoPVB+EG7Ky2SDPg6c6&+LLkStDx z(W_Z85fUn9As0p+ziNY#Q;An)e^hVVQ)#sa^A`Wms4FRG8h=9k zT5!eC(n`YXo@lN1^pNi=s)21frEM{=-Rc<^qK>DA-~xHY_F(Qf;eQAVuO*$%tw|oI zaz~|mpaZ2W$?BE0&sr`OLl?Va{*kCjK4SC+wu3h?KG5-1P0Uhgb_Hd%2&cfqm&_r< zU?EmN{JSf0mj{#~QG#LAQ21m2B_#nc1=m(NwAB?@ioic>fbpTnY}Z#@IyVVM<19Ih zeX;vC)ZIPsB8)0K9y-D0^N^MfyGQGT_Tm5NN`cm(UFO4kT^=ZEu=|bwB`MRNHFEjO zCZOQ|N>Jt!2R~1_e16Bs@wamvL^;$%Kj7LDVd68;RgP7K5A&hR`<(a_F2`{TlO}_+C3ia^0gVNx2$P;C=nFXyc2cYa^u3N+M;aV+PiGHS2t6y6lUr$>K{b`=JJ&8+BD5J%fI^Lan3f`7vxxzsLHztMb-I>Uav;G(|QV;G3H& z>k3h?37IR&!AD{mI53x&1Ycr}$95&;*2v@kp|kH{-TKL1v=;fs8E0S9;|4@f{G|)X z0dTok`Sc^hn_fLJ=z)`m{kW}~_|iFKbG3N6Tm3#jhets@ zO5U%~V8}k^AwQ-e1zDh$(%$3@*79(H8fRd2zLl<#IbH`DJ@L42nBOciR?$L}62=2j{_LS@N)@eGjlOS1K=z)~A{$jv_r zd|wE;j3U7;n^V$XQ5aKfpCih~F zUeRmQ@+qAEhhl2%PU8Blc+k^Z5rHG!Gc@~(7>4~9Rf;SvR-tc3>&7tq&*Tc?~ zNqr)o<5~`KE)E)p($P&ZH3X65I>PQw%TFhBGTWD+!9ddByqGl7J5@HeM)o@wIEE;i z_FhX}9M_aYh*qN2A$5|cb!efbf{a=fy!~uAPy}8aOe#=0SM6HB4 z&ClYTkM20W-zt(DnRcB4)iU@}4o+#TkSosPi!ItBY)mwP`^NV`mFuw~My)n1E^ANk zI-iVXeGpsxaoo3maw=zb47HHZcfAxto>5Rf&R8FvZ|jd(L%dWl!7R<63`O4lbP$?7 z6tM704X<<7iBEKBx^F@_`86RaS(fjsBq!7UoiLFiTIeq{F2JcEO|e;>;1@q$)o&nb zg^vtOWJg(fwSWCY{Id~EDF>rF!l@{WBSW{_m0c+A6L>RM4tOYpaRPd}DTmkQ<>K1k z-1WFJ$w~b5{;b;v1#T16q0NE7 zvY|534d>|_c6%_WYSBq9#&&&f(z&OwLiXPgN@70#>M>3!dvh&*$w|xmgdEgsRIxaD zwGj#X8F#@hSM;o;vihyHCO8M3UAX%y1PKKT-w&gL_dyK|0%L+cBbR3q1)85OdE@*7 zT4U;K=u25m5GtBmqtQPrL$-I6Ik{n<69?a{o%o7Kb=M_Xc)4|$BBXp`ZA|TRq_puwu&J|ppdyzv4)XH=T)>p zDXD%d^qXJjp%5UmIY5FOWP-ZUj%YcTGFSP(8w^rI{|DgJtj?aci>ytfyi> z9n?7|FbL*+QoeLMKJ$QN-!79F4W9+!a{)x4j^9$J=gEQe=I4Hl zbUJPZ+$&DdS*$GJj()V7ni5xLqu0oMmt9@M)ay87s;v+HHo3B8^4k^NtH|XrE}yeH zKl^Q@eWxk)+zuZZKaEq`J9;o#Ofo|efzC~`#E08iIo!ZLBC1~e;mdt**@nJu~CtJy}l+G97WPd(*m#vI?VVv|#@fxX`b<}0?1XnK1d(9oe&&{2p z4rr^ly#*tzoo{G)7Coj=FXtKt^l8-Vdj)-wpOqv8<(3%VeB(g$R3CnB&okjgc35r% zcU3gb$hCTwvSAfo-k@YM2ow0r)h2FRJMvs{ivGu%qE&ikyb}wBmV;6%s@s#1+uoGm zo*Y2=hsV~K?=IgyMN2w1URNGJ*OeSYJv_5K{G_FI+K$pFskgeuB_1Wlci!<~LNTj_ zR^$0^FOcnJ6Va<2yj@PxMFzUx%qaxA{7A=j`Q6kDi3<o(D3&F)KFk5k*WBAt%Kt?e$v9q2eek^9-~xQNsnMm?`+^67SM;4xLi zCUlF}?|36q6_R{QJTBFJqD1wMg{gY5LJc7@1(&T}T@o76b$%>HZ)5zratL#`E3)dC z0CbG)if=<(RK@dZJxaD(ZEGLTN?)e8yKKIjQ)-$%Lt-p;bABG^GpW43^-jw=MJqqU z0oL8S=+e7@%?WJV?K^nNH7|k#Ll#KsfLurlQuYdku7!^Grr+B`0=a~v@tt$)HC95+ zIUNV<=n=9$q0TX7O52vOj>J{Ws^K%Bd<|x25O$GgB}*zZ&_RY1UGupib!{qm<|JA> zsCKF$6K`DBPCq0|O7Ah3FkPqMu5*<9or}aOSRX*?>{4`LE_~v~q=Z7vTgfhT+73Oe zncbm>l8tVJQv}G=MdfPJ@{)%Oh}PF9tqn*`V2~9ey^$P83f<5&(W1fzA}`j`zI$M)0vj4eVEySrfJ6?)03a zlKwuOGojyre2Q|`k^;ohWtx=3I%HVVuH;#G4lAMz6?lMhNL?OCvdN4ct_@BARc&QV zIMg_Fk8d?riZt8BS4`k1i*DJYb}S>FQw>M>Fp^sKDCM#K=yZs|uz!!S)mkLzK3aoVASPTO}HhRfS7Vk?@|*;>#vZ=L{6{T$uvOK0Wgel3?Gf(}N{I74;HrkCc-5 zeK}`@RFdo(zE&1%LtLH%&p)`!c35$)z;!vhw3xMbBuxnW@zpgj)p9vYrB*tdN?jYe ztRpkaiWMBMPfmb1q)>i~)E#RX*3p?yu3*9pHixw~+YzJYYHTJcQxf9Z0;nCZeVu0! z5ZvcJT;*x<6xv;RX(4Ixq;8`iJR2XgHGX$OsZ&Dfs9x}QL7;W9tILf>u)|aI2J&GJ zt1C(i5s1=!(!#KNEH6!KwZ#E$mC|F*Zn8|2+im#f?A7H5i@@@ol%oK5?^&JM~2}{!_+nw+v4Vu z12;N`@o}L{K|h%7s>R}J9M#3rD6?by&&FZ|hhQX-sl6#0EOit}#`#z4PEy=sxJZhLl9u($GhXGd_i>y}gFBA#F4H`3*B)N@w;Ob#^eN|k~uW~CU5f%GwKRJA^DP!ORT=CQ^ zb*>E=E8i|vF_^XwPDd%TrALj9ZC|vuvu)x3vjgee+kJ0b&m`r+68oN0{n$0<;P&m^ z9#3`7Y#Z|c7ebXCFWIWOM0E??^yO4{@W;!WQQ}}h4#w8%KTXhk4wwRe55>Gq&&8_o zt)3pEm9oTv9}o*+@P1Ci|K50Qa$P*kVfjb0xBjbwEl!4viAsZ{x2NLg1lba|3pNA) zX`LP$!YRW&4)wl-|8NV~tKR2-J%RPNZ(8V4iWp0<*v&Kj<;#nsza3`Y`7z$(T*|j} z1b0+tJ0v{c6o9%8T+{W6f}`oQg>8@Y&e>llxQ%{sw_)sjiaovPK(yfF=?470boNA0 zjd$*!p8nn;qQb|Ou-^{<*0DU?T@(M_!80zJ`8@v(X-`|Wml`y${$?dtoKBJUK9%@; zt>e8S*Oa~z_qQ|MeS&+xQ-=R_Q|_H!o%z4H9hSOlVPaUJ7eRFFB zfBdnXqgSN<4(EVWBY2rlu(nr1H%McjOT*@}jk0sw0|?q@nSoGG2JR{evj>WO{%qZ+qRD9Ot3nh%w+)x+&Ol_5NM^VkFC}v~YiX1K7oLOL3LVz=SBOm#lpgl=OWI z3n6xw)$Ac(c4rHJb#?=|cxZ_|cMmTS8+Mk8fDi^k%`Zu}dXhVn_2q~#EA!(w;&DUu zb4_Q)yFzJh++>hkRM=X;t&xX0Cu;qs>=qd{p_j$gblmuarGBM1*bgfDh;-<#P<&}h z2_4Da1#3*qY?znE)Cnx0(bLAQb-n3ID>6Li#d8`p#mS4R(yrUb`&@;pxdH)kZk;jWuX{oRg+G7Kc2+O%3<`N;jo&-Q_ni zWr(pk#~c>=?0C-TY%*xLrlc7!Bb-84*O4^d0ieG_{8(~EO-M@Wd8XPc7TSN2=rVa? z%XGrf1g*URrE8WgeA#Ihc=Wj|E>JGQ zLx4;Pq|1>WzIP9&_?TqSK+O6IAJS&i0fjyKdE68aRWwSxG@n>-R;bwln37|U@g1j( z$yvNeT%5|y;A_`}F>R;E{E~L@MkVhzg8G4yYCHj+H-;;e7LxNK68UHMhgs}RKRQeX z^9e45DqddTpk0Qhgu{FUKq9Z!+b9cow!T5bE?kR5zka@m0bD?W^XD#Qx$;e-Z0VCsPKLJp$iY0h%DA=b5(f@$jr*~~rl zRH#dDweE%f&5wco%FvDt*YL|m`UPP>Qc+u_wKKQcp@E2tb&bp!J=@C!^$j$s!28FX zJHGEB|4TxyE*mu!I`+^mQL*qYEwWu12ynVVUB@i5x|cW>Df-zhq63T-mjyEfW)DUe zpn0rWCc{I?FdnStPSkzu(uJ~}K^BD0HiRaw%~Z&Z4{u_Y?d!S7P$vd}KS*+}ojy&% zPUWPAd;3=%lrcrAIJL$tCPt6q)rSQWvr!EvQyMLDXy-paeB~Z5Qoyw*KSeS6Oo!cs zwHgA41sj4RYtrB&LtcIzbut)ZX|4g}yC^!#M-Vv{Z(q?NctTBOQbcEIAhqL{L5b*u zbc4Io+zOXkQ{Jnalos3_T5fsxSJ0?UYRCI|r*M$I=}!ydF~pp}4&uYN*X6#w=58cW zYzQtUnZXub)aIv)&w8qeU5@Mt6z6Bz`xrRoASTm2 z`4@LuaL0~g2qh;qTPW*o>R+eULt%C2E34X{U=e{yQ@8pTPhjNa@Z(=zRG%VkvWUD~ z_(ar?6>`^gMdgFOZ7dN?8?}Nq=38k>-e__~Fq=+OvhpUo9C;0>3s=wq3*dg;C0|75 zio@p?$`bd*k+)x8sIa5rEpseEXzup_?-dq{p;P2qZm z9~zl?hD>Eb!`9{VdQ;eHd2GDMx#8{sbkPo2PvUm~zFYtr-t9j+p`WL+SXJ@bK<*ma z?Ti2T0G{!zfYZOlZoBZLY9wJdY0ghwC$)A4FHaDO9JI&W(=WgX3VlC>LOsAX=rE`- z;vVke8xyzsGn=Ml4%mZP{^)8Qg+;8u+rO~B95vlT&UBX4@cL|XP8P|JIT-vROYvjB z5lxcs>+;Y3fF9i1r%W%i)2y@JXw82vu-k2>zS`fg9n=20>G?-_({z@2 zq6!H+k6)e->vys=FkWpt|IS>a$mf3>QmRacK&0i}WIuv$RK0=3yV1^)AF9*2zrdP% z%}1ud0mu8v)%UyM_!+~OBgq`BGgU@k_>oZKUFKKMdvGmMy3QKB!gqo&PC=`O+oPej zb{R?bFGCKSW4)TkYG^~Faz~TGK;kpzWOj}HlW&KylrG7FAz0x9qtP)p3gy%>z^49X z^Wya5A&o<-rhbe}y`N00@kGehiFe4_P@7+u51bua_GBe`pWP2tz2p4AeDMkG_SjDO z?@rz3C~=QPX6yk@&(PUt1SPlT&OwkqKRZiM(bJ82KDeYLG&k0l>j;In^$XzA!wXHh zU&m_uczuQa9XXT(?Ffdo-js_HniQ>F308;;>WoQC9HC%4fBicts`f5$}422Cck-DT5za8 z@}_`AuyflB?NHbrOqQ30f#&&dRqej{GPA!#5P!5DhO!=%vcH}2jm~5?Y_ucBzr9Wh z7tI+I&9j1@=L@6K#Ak*wPxN8xF_DiBas4!yM#O;Yaejb53&x_bSS{G)7sGoAW9xXk zsE1{4Vl(G17;O`%r#d_I>H9?z-YniOX?KO=Xih497J-`!G&Bmc-(1PFk)G?En9g@t z*<2y=I~KKc29L(N);oWw*^7xOBWFedQRhyoTZ~RzoO>{96I(zYgTzxN(vzgN_x)a3 z;Z~Zb65QS&u4)#H<_@!zTEN`3VAAIiVsffQsO3h?{&?)T+{E+?2iVeN0T;S9PVaLM zU31PrZ*h<`A6`CNknYw-zrDzeyzT0%HEyL)nsTNubEr5jK|&UC=ksFW!zTrE19Gm2 ziGvCf(cV$}zhA9|ffv(jQfzR6daGOc893+dlBo@}iz(Cgd5&DjwbQr4X9r@(1lqgH zAN=-su-jV@e%&;0ERK{<#?3FDa!6SlvQqZz;F6s47O}ORl)AN4hmg&Jt(64AfgX=n zs?0`~T@F!;lyLa%?4{*bOA+mkfWtF!;;E#-9d$!E+%4TamdCc+OZj$ z9|1p3hBOE@k>1Lfrj$9g2DX3A%#^5h`Aq9 z>Upbz)Pc5+Ax^d!v^hrJx$X?fGaXY{o9-Q%?nS)SSXp8$C!t+^cAxv!TE?)lLjBy$ zq^WI0uIL(S{~`pXN)?}My5iKg-q!yHE+(z%^QUFh;zU=frZ00LvEW+!8W*9%$I`T6 z@+_aa=t#+)xifBtfPQVFA;7cWOno1)i}T%>jl-^h(|G(giiN(eS!XbY68p%owpIqf zIX9SXNYI%Wkauo+!-qLDo8g|5&N;OM#}4QB!|JMMBde*z zw;oBiwuDX`+IP~TPxOg1cZ!1@V$oA8v;2$vde3Gk6oiUkRHc1J>U zRA9S}0R(9)F=`HQF)Xoi;S&@;`8?kMg8go+y9DI{!2VbVFv5g;qS5#rHviCK00T@`61jG`X3ptMG{() zKD2Soc30t5Yw?B7AQt<*dSRH!Y>;H1_QA~6^|z+CR3BgKJ!MWVJheK22kzC&uA!f=5`bPGVa|qbX)kWBK#n11r-7SW zkwWn)jp6)A*svXe9%koB9BGaUXLSc)%yRw|Cs-ZZ=gv`f?M$|+a6z=6(v8h^5ARNG zfOiR|>VCE9l$jK>N!Hs3(zgGSBUL}mLa+=L?`-9qn+@VxFj=&(MO|`=>XMtMF~T8BF3Y+Q@$UWPUAA3 zY(EFj(~bW@Gh*ik|G#=4)cfi*jm9*cwFI=^)Elkf{PxebL~W_{-07N#l#Qp&T#^wtd)LbHHkB-W?*%17pm3Btrh%DjT4ClK+ zsg1CQ_Sw(j*Gp|)x~qKJUenw5R9-r$pKO$O%eRkvyNkD25I*GwIJw#QI;th;woS=J z_X&bhkn>3-Rc{Ywu$1wRN0`fjE)@6V@%=Wrz!$=(PxfH)KEgFk0=#Cv7&z&EEV4+b zS_FyVlwv!W87eF4doXr`(A!V>qv^i?b>&UKUd#ypyB9}gkm3Bp%A)eN`#8(Q22Otv zXHC?lF>LSIMRXq-OshxWw|%Ud;SifNVrS^`j8#SG7Ux@P6h`TXP$U6mLoOBv&H|xu*Q|_x@!J_mQRuTA=O4#X;AM zgvt`>&<({WqFh$6(s^dY=?eQ#3hqIdHjJrq#<1EC8QPN-6`hg43~+AilXos;(TSof z6$3`CH)>-z)gFUyBN&|eT3dX!Kc&nm7vw2Pbw*tVp2~#gFN0Q!O0adU2wS(eP)=cO zYmn^->o4Dfg~uol$ZAawYTR;9nqT!KRPU8L^9fv0c+@kyq<;C?as)&wqq5a{)wX!E zz`M%O0|bjziL{o>kCTgP-$zQs*0hlyYX=V=t#QXgo^ zmw&Q=X{MmYPp0p!+LFnz1?#T<2>+1+rkhpDYpLTF+GNW*fmY>gnQ4h1y_^cVL{g%( zy@|R1oBsJahsN+mO+yAz-hjBv&K))z(oPA6Z6J|f5ojSsnylhKLVgD5z_|kTXW6A;lX36#DwMbi~SVgsFh|T1?;hR#H|cX z-xi(44&}zc#qLal#_~#tU(rQkXJtblAH~U>{GJ`3$Cifyetz=X03rJ+W>G7#y0Sr( zheO4cjM4TaWv3Pw+0^*&$5fhF zt0(l_8RnOkkf*IifB+~26a8i_n8L3y9By^2h#V)W{Zf5acWA($J`gxjVs0*sDi2y) zcoUU+nc)Nw9Qeawzu8~5V7sNynNh;P?!pF8O7Qvl7j8B`rFenCtcP=VMf!0|KZq2a ztj8gnTU-tuHnpCXi-OlZuCrlI_GEwc;aZwg_iqRn)@8=82lX_+$5Hlm(FODZ54ZwerHZZsA#O@v1fURY3nc zFRTZWfFV;w6>f!gnBv+kk%jfP=TrLYQPLs%#_x%H5%d3*2cVK3tG_S+f%g=z`hgkv z^>kj_p@tDw(Y1DA1(^b^{N;dt#XN=dHp!ABuEUm4>8aKJwH}Le?o94PH)I>?$y$XH z2P-D(8SQpAG>0q@D@-}|b-N+DdjQQRXyP7BT;9I^#5?bap1|9{HcaT-JcTrWUBBk$ z%Zvk68FOeICeH7Am7<(>G{Oy*zGSuDWJe^-mCr0)Dv|T<3YH&6*ALNU7j|6r5BA}9 zRGw*aXmDy_ssJ{D7KgYXP!!Y{DP`Md@QA%k#PbU4++7e`pN$n)KTgzFSK+?wLEkqn zX8TuUoUumjK2&7r?(XiD?F32m_Z2^A*z|j<-&I#LzNX5vyezSB|2dxb;z~s|)HrLs zYc>gC`RAtmu3SB9vZS!-Ebfe+&x!~2cuio~J*gH1ovcx5H^tSW_iTnnO5Z7-&q2O@Il znKpp$m4&z2n(bOWYVMjt{6nHRrQ+?Rkz#OyX0a>b?k|RZ{rnm)(q*K@=Vu#{bWceA zL#*3T$Mvjkuy0?PCz@z99FEWg(fc<$Dv6Vq&VPRJBZtu~YFh2-4q(#5; zZvhCqHUfmBJB5Zsmv!|gOjp+P6daoLa}qP@+e%dOc?i>w@isTZUd;9kV}a`~vfixO z_1gjq(9#Ei@qQuoob@vO%_&G0s~29oa`a+B!{FFo2zxVeYMsCPywu!lLn%4tfg*#C zK=mhBMj!x$`8JIjqN;1r{ILC7?x90!C8#MaTIJST)PXlQ5%ML=>o0?LSW-UG!9tiK zu!lNTS2iH@S2`F{`L_2?mDEC&1ocB5fs%To=T4IO{9KvE3ZMr=79J7EA^OUknuIRm)s1pq zh!0H%%t`XYUsbfvv5xzZOF}}PgdaAdf1pWRJTrDxp~i0V^cCUC5q`U#;UxzV4klrF z*I9Rvl*H&isdDe;-D0rZ-bd~Og)EZ_E};CWfA9ECk?{=<0GgEr7e(I}% z&4jxi5JXyTNH zq^nn+!`u4SWpY7MI#ocAWNWYc_00{Fg;B1^y^26tmoV}HzSw-IuL!kT<~zg z2vgd#VYv?i)uP^$!|E4QR=WD%jgznjN)2^WfiS!L^avlBb|QeJh1t(D%vYjYP@M zV#7Xw5#$Fd%AdAxMYoC~wKi59e~+?ghWWnFKoTWCIdMV}|r;q$8wZgi$AoUdfjoa!E)K2Z==db!f>uwF6d!XTn zxH37SG@V0}OZt_mwja@K&vY!3h~P~DF1^(Tig~x^GpQghg7TOE57iRRc7{L^CnFJw zwF;JtlCEy1ZWbx_%x8U`QQp!z+0_@~RD{o0{Xp?Pu<-t!lqhCpdEwEb zlEiEl5xUxDxfA1yPv=?5v1X9|?m9B70x&`4cUiIh74rJXgoaN^(uiltpPtyd3@f~< z)`9iiMhbqtNtVk&$3Mz+$#_51s?OeyZ|5jqK(%ZBZUv#n_Zb1~@q9z3rvBEA^eyV5 zQ6K9buy88En%UkI)D|2^mhZI%5@^DP>EhW#pgzs7A?Sw}O8uL;J2=18vays9U*}*` z1~WC!-3WHeSHA1#zrm3D^tr6w<{N2AeCQ_n>t8uN#gLD8Ri|Q;$n90Vu|X-Rb(LA* z$#!qZ087~kDE5LJ+3N$D={GPv{j$VXb9vWw_7#9ku=5Ao^e=;*8tg&OGec4amXeh!IRz2qPCXKi7(zI%{^A`Z^XIr_+&&sp1(N1zEfR@3D{<+{6 zk9ckO&V7=_&;$ZJsZ%G9v(6@uNlgq1?|^}OT9@(tE)b|;Xk_oto8_^9vy8w>E{F7! z+O)m`&}G!s9y=GpmFO;GZLSxq>8QkCX$~u|MHbI{gW?&_jn3`+CHLB@02XM^xQcY|m5QrY zY)$B@z)J2aoU|lV@uL{Xl@FSCFmt!yX??d-s^474x|GEft~e&PMQoq2&Zum@=)Pb#t1^~>QU#_uXwCnuEO_W7PDleb&?uh9Srljs6(SF_dGnVRNqV7d3{3sO7y6HoBLaa#yK;P!EVl7hm1>rM^1Gmx`F_;WhXmRk= zi(qsFTV4D_#Dp*X&te~8p6$%Q>RkI%li$*+&;5A&`&RHor3UMSYlcO;T}0NJN?GQMc)-5P@4V{Cf$ z4QCIu!s^j0cDLSOB>hxVAMO@z*^7T?WO(g;I-5U}aG+JcNe)=== z0y;nEdb^Y&^!^8yrVT9m(@5miZ9)*{gM9W=avtz!y$ualh#t zt7ZO^=?a{+#l#Be9(%ev?i(s4mFwzo_V0+)PCTs@j@yZe7d?@?LH7*FA1Iigq&Dq4 z)AdYMK0izzi>KKeuu6*aan9(UwhGz*6^*|TQbbX4s2yS<=!nJ8;*+B3KilE;V~L;$ z1hOldZUx%+9`y@#G^7$mplXZ;o?fPH=1x5oO+F6dx~_>ZwtssMp)EOT=r~8d==-otxzkBw+W2WA2R(qMv!8YaX;F zWY2QqwJI^2bKq0b6L7J!o>+54Y-bo_SJ9XuOU*Z)ck27%En84mcGai1 zh4dbwLvKBRFyF5ITKrSCzhoz9U3HF*6om>Z`*q&p+Yj8z_Zx}_WTw*|)eu6~Jho8g zX^VBHTy8=2rv4@S?`(p*d(dL$&f5?^&JsZu>%0{CnEK;IiMnHz@Oh?C)h4J;+isid zmBsvyVo&o(C5Ui4@if%t!40w$is>WGQ>g>+TW>=zEdt1N1NE^n+Qp(86Zhf%_3>UT zvLAYF@2cZ$!&b=U_~oQ!n}E9IRtsR&>|!C96HF-+V`;h_!1=%r*>IIg5}YfSb%V8% z^)9N|RL^|>X%Jr`)WVJX!l~R~XL1FcGpLq7mu>6d-pbQA@s7^QaW1rGdHfQUc(dH7 z++k-z@hUh@@%>-Ty?Hp>>-#R+N!z7WUCXpuMZ2n#DV;Qm&{ikZSVPTJQ8Y1AA#_o+ zRkReXd7eUIiYdfWMQfg7NQ9zF0`|2hGu7*QSV071xQ{S~(^9089C`V=QsAkoQnzcV zDZbO3`Fcpe-TpQ85nFj$()`2ionL*)36-(u?g~M33PrbZd^-&QBmCn9Cvwi`Z&me# z*2R<>C5)(BHS5uZcKd*ja-fgFEhK%+39H{2{K+JgKUfi9Z}z0%J5ykyC+ObF z8(<4RwQ@|&H}AhbmP_J{=Xn4sFK?6u{YAnh!Ma`Xb_eT^W3|QSxE`*GkQ#Uy(oe^-{h;{!t z+HO-$zB##D#b{OR+ycfZ+q*#2gtc1ApCbnzW6%u6KHbV2^=JJ`{qy8(a1uu#@HOp> zU=FGXB~B#V>5G(56<@agDXRur2*ExhK_L#{5@shd5h$gW1TK>Ggy=$xqyBn;kQ!A_tMr-*rj?6?=W^$WQKl~n-5 z^5)YYc=Z)Zw;3sbb(qK|{q+qIhRDEsC<*${HfX5cD}%0)DruK+uH(1nmE~gf<|WC8 z)D5sSp-Fc0A?ZPOVHUspx-#cCVfDqw$0^11b3P>DN)?A6qVn_5VV0iD zai1|$J!*(dNY`%lNRK+n`<#x73taU0QNg#yBps7VK87alp%}VA$FDuVD9^`YB-JP< zU%)RV5dFJyDSvI<`)I4Q3n#t0(re;- zU=c3IGOpz<6~@_7``mJB48>aNIwh}DIazfQk?Vf_>tC*qmdoLN=8r@^tVGRhth7pF zzYxx{bG2`>e%wOIvmSAMog0CtrC0IfzKD!`bLZFT(5 zI$@1it(T(Q3+f03TKTo4k0jtV^KSp3;>i?1(p;4`R;g$7oKMMsVb_6CxL6i#*a}r$Yx!0q7?on3~bxU?E29#_xlQ zX{BpR)I`9Y9BBClaN>4=FY>^iqwJR2>kzqhK4s61-bC%t!$u9viKg)21KJjk0GHbr zTB}#`{CQ_Cl6P0n1)T;2%(b!zP)k#_tbr73B_yZ96~*&nRL81yc(ew9&MZKJ0g>dZ z|9X~yjB$jsf9z_RVcj#Z2I?;!5|){{jP2rszJ-!2;8${WQsoG~BXVjklKk;LF4_}sYF_uS zhb>KPNc`iAdUK*a^{W%%6W3;Pt=2~Xsz3N3@mhyc>dU<^2L^iVTOyO2E;W=x2y4@# zOZ^XZ{HWN_W^vAVm_$&Q`fqX7s&LNtVE#9OK6?v_HQKrgEGb^yi~TBwP*NEC&V5ql zKSb4s_QY%%x*<8uJBv}uv0yx4z=;J8c+@jE9!}s&7`d<#h?lVIBQ;)$+hp0BRBX)} zSgai&_p>*`T8iOO)ns8WOQvkE%57thBOO70waveJsgwkk<7-vM8R`s8xV#1RgcO*%cek5%ARk_ss} zoqS-RC|rDy1hd3*2;Z{TQ=s#s->1xv$B@PAy~RL&;mn>N8=FE-3m_cWNl&qEZ2vq) zKYQTuF~`nEoZ@t!`^wD|1bV=8)zDnc)ubQV=%$jmWt04owLdiaeI?sCIburQA4Z_F zR$@w!x@G-gXOk-J0u*~rZb#4yN(r~=sprL|fSzh%m$Zn_U#{0o|G-HT$~nYhQe0w3 zM)1%FZo{I|D`jWG6NM;wGYz^>9AWBJg&vufI96m#`BBZ!kYj{bK|9>4p@Y27g_hS_ zLxx3t-5SH(q0xHKw?;-R8m7_M3MHg$aEBN{E2{Rr{cXeKTY+Uf#LANWdryVfb@!}k z{s}oeT(2b-Jqo}c+knm_{2R!C6kz{Erx9i&zBI!m=Le5))NYxo7UvUs)GL16!{Ie4 zp>R@p^=dc9Iq$Qjl2&?Kx?0$qHf4|L2i?MyHNON-b}B{y%r{fytztzR!Vsu@R-+4pJs`z__V{#j1p);E`46dFDJdCM|>|U1Ph(}3WCI<@ld2X@@;*ycM zrF=$Y%tZ^RLl>d0ZG-ufL|M_oxDN`%M>?W@&9fbg2q6PBHz@szkaW;3v<|hW!aa}2t z?E(aMNwhVzy{?4F(`9_Nkd!Fo7N34754mTC{FqWqBm37qZPr#rEWKru25A>so7A_aOFRu)#&>VY1hD#0heXc8c zyY&57T#hBB%K%^V{Ab2+wTE#r1gFC~`dFe}UhCAKF4u{z8J0wW`%A-cM`yD5Tp$jD zyCNpRz9P8#!S88%G^IkJB}WHFbWX4x4RtQ4Zw%v4RGP0XtC}Y^8y7zMRYtSUyK#wA z_4iJnFt;@)xe!y-oEu;Pla^Mf=nAipAR3>Os#$s%3_7EERrE++5kB3e?*5YWJ8Fs_ z<~ij1=AJK8%zLqVz9D(+l$+GutbsuXJjbLZE+_`v(#SHV%dX#ijToBL9}jc253~*Z z+*qe$MRLs$iNZLwWETTt#g!RZj`uNBNXXK-U4M|@=&LfUJS9|%ePVsoZ~njv9B?#Z zY-A>?#MgsSp+?mTc~!vqbj4vmM=x-&eu=tuO=15WOK-5+b-q^ygEi58k>xSKW?&N! zh(AxuIbXb%^g9YSrcQ8lN1su>Sh85J0A8fOw8$dHeATXmo|L0}CA%QnU}>}4QESJpAY@bUQn;@A6DvqR@NM{~t_L5Nh`GdM? z8C!#Ee~H`}*b7zG9TK1#pQK%IuJme)lO4V6t?EQ33i~%2ydpf%>mE-OzO&oN;iA{& z)idEr6nG!+Q^AupeNEiov{hp^?CZ3`t%~jVCyEu z;sm;_&KUGeIy=Mrp<`+dqn-WNax&ese#79)Q31zzy$;_hqZWUoXv-MMPUWdihKZQ7 ziHdYJy`KVfv7bx2j;;5e899q{Dd)dx!MV+8P6qRm1;Xzy|At@GSUEiiUVlBpG?b7m z=ks1is-v+?r#DfE)vIgL6Ub>jzuFT~+B&metKppzrRW~(rQ!TRpPDLYgPY5PN&P4@ z(ci21aflaFh(vN7pQ+cdiCfOfO0)V$WA# zD2n~QJ)3*7%y&7gWS(DWbLUY0Y7{wG1z#U3v3(C~n8R9mqWZmhjn8VTW3f!{yG5Ct zCU}Cgcidb6f92_X_Btt9gPuy=#|R^aZ)yNt+0wCiBNiVMW@=s*T zZhL*_!wM|cTbthhAbcyD&+~)!i14w8Kd(HS9Z)9$BKmJU1r)OC&nM*BRgJaZWhvj! z=%qHr{;=~G97YkLXh17P2^LW(H?58^ zihg#l8uj;e&}nE?xW=~}c4+z2dLYbo_N*?MI-1uqJV;cL%*p=;Q7@>lq@?uFe1B+r zcu|9&8-Zc#Yo9zcA|tviRld-X=%e0gXp*eve5&)O=)Hsr#H>HM_$wx?vq?|2spuRN zd&8i!A8OOjT1lD!o%d7Q0(u@FuF3V)qwh*{nk*&hQD>8e;<8?*T_8RNr zCSct4$QLMHu>9rAdBqC>Q6B!@E!Wj1*9tIjY^z2qqfcf4+Htv5fq!t~W0yt(>NkfJ zcWRbQBuOH`3owG30BlUix|E0mK1y9Z*lVExyzWXpAv15?PXjN~>37j;Mq` zjB;c9%8#Tj(N7JH>e8ql^FV9hDu7Ar;ogOH>SRb$s*Iv@EGPZ#qmkNZ5;EdSy#YST z@7DG?=eum3Vf{IhI-PmoQ?_P2lG;@!Ni8VsOvfEvSw%}~nfX*tnzBPjYbG|IkoW(_ z4wte3^%}+FQOF7s_XJD8RUI!OFI>V$>Gwc@T;6_t`PdmdDO_U}9MVypbsP+XTV!Qc z+XFSoXz9(UJwA1B^&t<+Ai8C(j1`3zhoY`yHAjX-9N&;w+jP{hgWHOuFg?-!m>VfX z2)AK+V#!3kOlP9~Av5U%NFcK;C%(G-s&{(1wHcY#JJGXp|0!#XlRHg!Nk4-Q_Z<;E zs$MEK#WqPozAfuQk8dVfk+Yy3v{R4jE(mHnrp{2k5IW)RW3^_n2yk53p0(t(Wpe<{G$e#i-JYCN?R5>v%IHYSIzY z^gCW}hk zmOk_P095zGvLd+O(O3CzH)U52y}Qck;S&@*JM1>mDEM*c1x7zj7bE+GXg{HWMBxVA zV^`q0k{0&*h)0IZjXaNhjGxrGaS!z*6l1_F$wN<{8IPT6E+hJOSla%*Wf$R&|2rzX4P20eW8riW*$X>|8B1`k25L#L?&Mp|kb7%lZeHf! zhkoL9NyokH4|YWTZ$h3m90RCqjjvm7%MNgRoV3LbwSc}uHoO&o#j-A(MLyMgz6z29 zk+v_@-wN5ME@{;@ueE2flJ4SI1qV_T_-`;TQN$o~-?q2Z0K9=uGWwa7rR4v0z>q%* z957se*kS3TC6ixh=H~OZjGC6%NHcCFDyI6gatKlpJA8R6$XLBhVlv?R^k~%T9{x%# z=??}9v)YxO$p*(PB!t%2TAy5)Wf}Gv$~h^WnY|oyd|Sj=&9Hj(fgUo$4vk%?&H~~x z;bl!eGNP4R(9G72bLU(QXKCTz%5*lGNbKyV0+dlJ?NQsJ$%RhErl|q1Y|h!0wi7GW zf5<4a-Jc_qkPkeP^W^{53G#vxkp1_f#2M}nSs-;6kr2<^8AMi$yr^lc-+iK1JB^6$ z06b1@pR7-^r`oi%z`f{1A(P_o4d3n5rLc+0W5^m?NuWlpYK!=1S5A!%h5KdBR*1e9 z#TanjecTC7y-4lT;YidLcO}@dreqALlHzA+#N6(CcAh6>L0TMtnU$A9lc!gF-`r3N zPt|FjV)i*nkP7tjwZi^uWyWGcR1^mojkR?uPKlJC%B(YF*h>3Sr-c3B&EBN2KG*t> zzrmxB495gcnv3DOzEnqmr{4=lz$p|dYl>mbz7Wx8Me_B`m*bc3D8+h1-LCViSTaVkKi+H2jkxVKaTsl z$+whyI5=^6mo{>&e_+7qTH3%RtJ&J4x#BUF=gLpu+a(9%BzMj~8X)Y0BT(Z}T$2xS zc}T|)gn?&Fk5~hy3ab4#86r_zt^)2=_s8!pEPYM;w>;S-kBI(ZnbLmVEhSY z7n9cacVn#;bf5aiB!!hJPnSh|ry493eqfcv5zC^HyU37qe}DS>CREJ% z+unYjoDX=HHxHxKu>z)%7WTc)n&y88eBn7-j;U?%ti1u$HDfDaZAz;_>t^;L+W`?{ z9(`o7B5K|9r6L3d0SBAAU< z0Z7Q0EN3ZCf%TEyeDxQ~3v#g*=b~~BTFgV{#JVX{%gG$*sA2}3lM?MJnVGb5QneRI;nBUUweJlp zZ`6*x+B8$U;279U$u%i-!L{YFm6NtNN;$Yk{=q2ImyN^~ro{GJ3hki}^0J4@{+=*m zfZV8_`&e!uQEm=<(tk-EoHEGdZr79EpY&iM=p8wVJ{sUlyH37AIldP+XI-R-+15{M z`S85l^@2I>ZLe2l&OFrX^iReI%%Pb%seh73?J2`%P@V0KW;Nmy)?ZKwyk`D!4tc?3 zHyn`I$B)r-skvvjy*421Y|rB>p!(Dpl*#|M7m4RNrJQUeoyNx{T+Z9B4qBYrt`xY= zo$2XvTKudTzKwhBxR#`?Y`a=y{ZlRi>TbI^;N^Dp{x?Gd9JK!TdYAkMk@7!pl`;#6 z0dbGn-t4~)L2AFt_VtZ;9Yzm&A#VQ{_kvtoos6opE6rK2gx_QU7fij8=ac(97JnEw z5ETBaq9~g78hOXJz>HWzyX}RkS&7xuV4>{_2!`{ZjHk||dhys2;KV*|Bi8nvNmH`H z>+%5`5z|c~(0yLNSJ-zPzv9#{Kf1Q-#!h5B=-qc}=nf#nl&Txdd~kX}lx96h0|o|l zs6KK7n6TH00vGdtXFR7*6xi&IR&_?CCG5HgKmTda8miT$4*t*0;>7m4l~J?9Z-gP1 zbHFGCof}L+9*h$TklIH?o|iOyM3WvAt-1*TPt*917lxf2k~P$6kUI{{(%+rfp+9y@ z{xh|SM;hd4yFqmiFv8v!WO4F(5ak)DcDnx$HwP61l}3AmDBw-b0LCzA(b@URwx`5)0ifG4Q+^&BK+C3s z?Cj=zEk=e$9B2aoxM9*gdOABNOigR0!#G0LtU|Ezy81!nkFW2o#fzvOT`=PfJYM4S zT)^vz{i$wipEF&H$pItGtJJPk{>>cJI)KnV?i6kiOo72<&7aTA3Eu}q=4eA38%vNk zEvB53!0r*(#?@$F3t~Uo`L7q!9i?_U`>6*}7>m$NGpe3gxT3`BG{ez!1wgI`=p0Z{ z_7MpG{n}pRfp1p0Fl4|*2P zeC#Q&-G}FOfxi=t|7_)R3@mS1?eKz!^qopDzA(7J*#{SaUy0!=nUtnAI}a>@N=-^0 z-?1^B;>0^+s;>bTLb{bqXT4*e{Ss$P)mR}e$B*ikZ-JK0uT>eulx_MkNM6%E^=40- z-&T(*xqNIL_8KmCeCTH3i75gJz;|sjP&>E1p&KIb&)yvuhCcElhtVwPXx4xO&4rXg zO$}7ZlV)7;v8>ddEV$E6V>VW`hGdbn;aJR1Z`D^SYSf@A#vg|qgDI|c>#YX7<6(!7 z9Y1lpEgk}KBr}%ECC>3HFT8L>i1*l8?t*Q*!?^>YzzV1{25W;6To(1YVYtj?rPC#3 z?_c0XAGhxfM)FT}b6yt}uma|(-&V!~C0<6cw=(yfr+lrkx&3iZP_mMbx9=`@$AmpR zqL0^~CLog9WbgF?HZkbe_zJuE(K@?ejuYLGUx!x|H6_b)<6+JN#$^}znE9$fYt5G?K&U&wr0Pj zkA?F$Pt8G-WHSRqVdWzcyW2?vL6{3k|eM$l>JWdHr(0hcu|z+}Kvq*VteQimPv}xpNG6*Rzw8 zp-~N(PPh(?^}$D6Q|!1CarddhbHS7<>JhP}zFgzY%RDz2|GHO5Jt*U^M;;vm%+BM# za>P?<7Iuxiuzg_?6BdXY8Kt~mwUZUa_U-3YS?)^ArpXxckqEmLP8CEa}`7GQFm^AC1Hh zPg*dzKU?Aj>t%ZCSH`w(xXL(jKQFCUv67yo8mPgW#37*eMrO5j_PEU;oWsUY`9jRu zCy>DK>U;P}oDL(5h&C1>zuplC21rz*nyx$0%AR%k5$igyuxm3J{nv)pqpLVomcf*Z zAA6ly3*;!8;%LLx;vqTpYaOsrcUql8Jh%Ny*M|+#XOTSxow?d|7ZlEV1sLBi zc&FYcXs?x`oc&}iEvU4y+WQvr_ zj=A-G%)PWfrU64*DKJC_tq~hX#ZQwY6k9ILRJOdwXaahfUV3;JWhbElM4s`VnS4Ql zytDS5$xM}#bQ=_lCV!^S0&y}U%$_HlV67hbu>1I?pS(DLKrX#|s4&j?R(8u2U-a9N zw5w5k*8~&R#8;OuW~J1#J^aa3On9qPitDi+0x?)ETXgCw=oP_B2wWWxv#zGq3FE{a z>4tE1xWG|NtAXy|_Yd)V4;*>k_Q-M14QU`K zO)V04WO|~0W>|hmz<>@s{j-k5{rHR;BR}`H8Gk#qyIMZEcL0zl;lFTGR|TPj&N>%l z;ihKBl{%%2YJO`LUeD~VKCPS;)R~^EQyKU^b5^mXs_zbbfN5nFWtE!MfDtX10HjL1 z_jpQTmePuw&wYV8jp}YAsgU|{FhaTW=NP<4;Ex1V!t!gU`>yij6RdbI93JRuW;m=O zMaZL{f%gF+G`A&PS7-34?7dX#q4Ya8A8(Q3;?Q+{l<(T7N(h2%SRHq+AdKCM$$@T@&Yp28eROFxCUnQJzh8>$T%772Xd$2t+m=3!=fkDl- z4O5T>>#hOzHf$)4o}QidIrKE|MSr_5e76OA^UtMY&1LhbA5r%QhYARn5&YV}gk>Rz zN*l1q=XWUWG-R-?2Feyzco`HJmO+bN<_syvqzvbn%_jUIge_YG!^e` zh&Ww9VgygLD-lQZTHmxjV__{sGaa zYkOv@M%=uIL1hG4NZe$~InEYoM;K^ap$O$V%Fv_sk)PR-cFjA^oYRN;&6d-NffU*-G=IYYAE6lj3HG zYDoqLdS4W**7&^YzU49IRky|pjhp%o-TlE@IWXlEgmJ2}ams!tBem@A2QlVl zwZd~n_2Uk;#oQO@pUrKdRh!7Qdl54%Czmtgsu>KSvl+mn~UXenu4^HpL>v41g`>w{mewYiW49Y3f&;89;ui={0*&SvsBa=q; zD#d|OeS60OuIF%bA%>s(iWC?_v0#(}e!bvGvT)JWDi$S58*hQ`1_?}2zpLs))1mro zDuwr>pxmrW>>k|^175U&(YWFB8YOeJkD(5$-1*cieK~jP4{Ow2C2AX5!ibZha23a3 z{COhS1iRC!IOQYnE`vzq9gteZkk=}vS3p?zAwGy#WnCj&p*YEB%x|Du*X^>p<;hoO zs=J)Rc2YH93XXX3>$w~6x5w&>h3aeGx)D3cUq?Hy-qb(i7^GNtxZ{X)da*koQ!K>4 zI9p(u$QHx=&1m;PHoWmHCnU5PwA66fYZW5wof=EGc?Je5rrg9h>4uD!->f4k(A0|2 zRkhnJ3H5RM`2#a7GPnjwyNmjiQ4i8MJkxKX#>#cbRxNOFD2=`P>UhCf1gzPy1tZD& znDs2ddr;IsV6J97Ug_g4L)a1Md$$BtUpShvF8f5Y5qRr#(hl7L-nFoP^%RNn_ z6-jo#Cr*`Sf!1w#C@FPS)%MPLfy%nGj?P9n#+Aa#19+NXx<`pcXryypQa9m7N5S-e zuAJo|#^JgValHD=$}(}}TtR${nC2rEI=*_Xl~@A^yFfR?g8Hi?sVL{7vVVO5*g8AXr2ynnf&0AAK`ke_?g0Lo z4LNsKPwu6ltTDu<&w+MG89GDM=|a^-&6b$q#qlQhdk17HQFcAHP-z$tytS;w=*UI+ z?*}LV?a;r$g|jP(^*i2i&wHmC>av@7HzTZ!iRh5UN;WyY}jr z`rBHQ!@%tZ7(~nx)<%kp^k+nb6oxN(6Xl#(y;6LAX}~4&%gW^|(0%RhZz(B9yxVYL zRg9d<{rHKM$D>Z~!C(i@6j0MSJddKm4tUbjs13s~E5BpJ{+pW1i_qF~qo|r4pBB6fGML7-Y zSv`4jrdHNXRR>-=Qc*=7N23)^RVr0fWWiE*eFZqf--l5-4ky%D$K|Z}{IZ+%n;cox zi2vKVdWI6kF#Lw39Q3xqMJUje$=L~(lHjqfGuhA~@^@o8Hu14fjq}78^=U@s7<=?E zzK$&TJ1O;3sN)zd)CAV<-zN)wG}x7%Dc&km8Rpfm8TlFFCnX}6Q)GRUNY5i48#0Y(CV;cBZ5-JwA?^k_a20z^-GX`(~{DbX?YPXYV zRY?EE!^rObM*N8R8i0jD zjptXYUOClMgyuGXMg?BoR`eH(@diJG(LN6P=6jGk_w!GVuQiS!F?PEiMVg&6N;+wD zDm+=oN@lpGoGNZ6p%@R_+E^O3dwHhl9x9G|OTVQ8W6M4yM|_R(U~kA!a8Y=)B_-8c zdPvWOpkZ||6v(?@h|)gL85h8uFK+=WApXc-I`G?N3SYAvd53be>+;*&`zIeU?%N3x zW}D#G4vQ%)6nwsP4W7J=NMiA(w7uXuwQi$X)RaMHrY0I*69 zw_Pj{`8gj@dGS$b&V_Env^*~vk)eTyAZ(z8?47wdRTn(tS{cH^0o$%&QzUxc3GeyL z@j9Ut_k&F!)+Lbivz_yD(3?tBGm9k1h)QO&6^4fxM#*K6^?hgfeZC5Ipevl{2I&}D zP8q`+F3l7XNx;A>TPovzHgM*5H^&5VL21~lSC-9B z!gvoZ@rdz4<`iO7*5?2t!?Q%&$_%=cUjYc0)}rw9Pu9K%H`+-QqsK!H#?ysIMo0B* z!twkQ1?AX&wT=G#&JyEBe->@O8dv~cbEH%%XO-jt?2U|A1@&vKHJ;i%)n}BkIzBuv zriN@ZM&PfmWWDKp4YFX-q)?Fmt@ZdYJ?JuWR+IvVQ6M|6ow`GnM9pSzF)^r-WboW* z6+K(`nvr;E_3Xd8_`K!MV#ON!?hO*$t=xf2k&k-Xk4(F#VyN^CKGn9KMb`CkUkj&9 z-r8qJ=Q16q2p`dBha$?v#4^(Ha64&mCyI*J>4w|$}1%S#jGV(T|+s>FD21EdXdkZ&^7QVQ?4 zf|%dz?to`oI>!Yh6uwh~vF6+boz7cpuXm>{CO}Rstl#2u#P%iMy#Kfn(Sc5RR-+ds z@K#-zPF}8FSNH69aJVyG6mT5V#TUiR4^tME3RSaeqa1K+8a8Gy3d>QC z2*?iSVreHVFK5KIa}n32z!>EF1;x37MiNCX@M`u)UX3h+0qrLM=iQfOi~NQ1VkAeByfvEt-Wap z8piczeQS!(nH|%pJ4mrM9v^-Ai$IVS9O%v~vIfxsqbfGHrZ^&exP_U_@L;Ze<2f!` zyp-1yK7UJsRbV(&u&Z}-d9)L2S;L!G?mt$mvi^-Q-BX)PFTH-SOA4+D46h1&im(IZ zP$Eg3m11nsw3cB~KJO(lJ`h2)Xuy>7xAa&-#vzwm8P4$B3gd=sQIa#o%X~fbX`Vdl zX8JWW>iB3}-lQQp-R4F)x1w;<46K@oWu{RSGWpzGK(~Xl*fV}hRE5Pg;H$T^1NxV4 zTj%YPscbL_%YMkyz^X(^dcB6+o}kfva(t(RP={zce6Pz*i9zR)7;8 zPz?Y0=zo1Pf5+l@uEQG|MMr?+!^@u zzwGb7^RoXNE%n}y{yuk2bs_E;jy2Z4Ku%rKj=KE(%hSHn^{o@#K>YgT4n4=Kzh*rr z6RC#5e>3MUZauz>S?yG+8JB0;uC#ec^<5!8Z-UG|PU zRU9ysM@0@m?E6-Q+q8&|IWrkQU(Q4-?A}{-lv0L?t4Vefa(-s075&WS<{BxQ1 znR)5UI5pI~HGG(`dClcm&(+wTi+smRfn$2yLGLmlp}8Z+6+f3NFWIK2YBK?7mZ9NNpi_ctL8BH^<C=u+oh2WvRJdMrJ3JGl*}WFHe3(a?Og9;`p{6+EC)+{Dr|V zy%^oO;$;CgK^145CRpi{6VwOZQsDl{Qc@b){Kib8@V>{|-_HhzMO-~C&MAl8C$EB@ z2V9qgaS!PJ$x+(;&0{*nW^;EoS_3bs<*FE%TYe~Szzpa>-W>{ib-dBA!F0)GOLxP} zMXzEsZnHW|>*<)21i8kMu!2hy53*ivK%Gf<0A2fZkEH;OwHw;J7!8pCP(o9e-+I!q z>P3G)S9<@?ccRn#=qL7-g?RCj#y;6@xX9yp7R_rbK8>1%)T~)r*{{bEUfyq*xXOhv>TY0@PrM8{9hWLM-1&Hj z+qxyboN#0_F0Bx*d2L8Gbomm}((IZ!-vPXnsI2qmnVFJ;Ynb}suD(S>2d3*Erh{oz zmb69vNnOZLoI~J|W-)Q((&?spvUed${zs^i=CmHaWvq+u`m>U-O0NIPka}7Da%t7L z4}3<3+;apcw>~KLa7f%ZmZY>Z<9}Ps0W?VDTI{5>6};H&@U!Hjow<*+Q(?5`llzS3 zTgN>t7vfe*8ZauV?Ub`t;A4J5Tc#aPIQ4woJkQwPbUT1a5_W#Fuz`B-gulH~p&_YPaLeVJ-^LpIA-OZrXOAF1N)m#{^nkkEiIU zw+fSc%T#?rYbr74F;*ukWJ4U!zf4mJ^I~tx4GzdZ3%=E#*HsB4%RhS!KfONZ4skd% z(D6%hU5oY`?$nXs$GE_O`cF1cS(!CIH}Rc#cT!|0yC~rEJ|J&J7B9FCJJi;r^CS3@ zck}xG1$0h2KH&q#ve_29tZOfy79Q3Z_ zo<7JM?8+(8X)iWG^KzP} z@*d`m{q)q-a)ZBNf!DizL?g9`J;yx6Sv%7Jq_9FM!Z^99>FtT%k?epL0>93Yim%N4 ziR8jZFYEhP6T`PoJ)e!?`YHS+6*qtIv5?FK>(W5}Tj$S#-DdB+#VI{iBnplbUzEe| zRWj=#&NkqUHeK>|D01STvfS)y&?JsruDdg04gEdm221F8J@K5n3Dnbon=fvdS}<7E zXlB4`Y%z69TPX#eeaV|L+vGhrJYfAb@|K@>OCv6Iem~xE#rU~tf53U@c6@m;`Y&Q^ zd@6xYT|Qv$T~8nW!?DZbDV_07A(lotx>5&ks!et{B^ia-@x}>W9IhAHTM}svT_tDm zlho?0k2Qj_SI`S#lgzjGo*d`0QI(liv6AO!We?VLeI>uzCC}T`Ev#~CUc6)7E6{d; zvCOljb~ggrv1F-bcr z$A=tgHn26O5q8Nf@?0f%sLLmq;>3=Zc2fP2cmRBi8}r-EwysC37n`M7m#(F+1Ygt; zRNT>N)C zTPfp5*!LlfgSY;Q6mM@Rt>8AkeYAe(=s`~F@;;(o6{5C08Kfxe5~O|9qYXD*=;`=m znYp$=X+A!I53G>L<7JPmnc7GCpSMM;GunTFCNf1k+IJ^M895dS`i)>4rU#{ql@ z-%Vo=mgd%R;0;sBcytO!-b)D}kOR;ZE2I8^yz&k(61$Kh+t-Nq;MVUv6XDVERVcTe zXtwEm^x6JD`*ODW#h(jpTAiAIM|bf4=fU)RWzhN8eI=cz(#} zuVi2)!OQU+dV7xS2S-N|g|BV7+&r3ZbE)@31g{w5orK$GO5TmWbJ}0?M`<8CqzrEg zEvj$^(^`;K&wiG<1V}y=p)o|CIggM18WmukN9~R89`53=9CKQUzs(3!AaA$CN?X}c z!GR1n;%3VPBs{bz~b>QBz{Q^L=x4O7{JFP+fGWLu3&X0b?LnMm9o8I|m zS6ci|KdbF;URZ|iq>I~MBai)^k%BL0?Ee4w$Hud^n_ICrjy#U>_u5XzcB$XdzKywM H9rQl{{HfUT literal 0 HcmV?d00001 From 69570bccaeeca9345adcf130fa827757a89a2103 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 19:14:55 +0000 Subject: [PATCH 09/23] Merge `Differences between Lua and JS` and `Limitations` pages to `Caveats` --- ...tween-lua-and-javascript.md => caveats.md} | 39 ++++++++++++--- docs/limitations.md | 50 ------------------- docs/supported-lua-versions.md | 5 -- sidebars.json | 4 +- 4 files changed, 32 insertions(+), 66 deletions(-) rename docs/{differences-between-lua-and-javascript.md => caveats.md} (50%) delete mode 100644 docs/limitations.md delete mode 100644 docs/supported-lua-versions.md diff --git a/docs/differences-between-lua-and-javascript.md b/docs/caveats.md similarity index 50% rename from docs/differences-between-lua-and-javascript.md rename to docs/caveats.md index d32e3e45..d3fdcb30 100644 --- a/docs/differences-between-lua-and-javascript.md +++ b/docs/caveats.md @@ -1,15 +1,30 @@ --- -title: Differences between Lua and JS +title: Caveats --- -TypeScript can be compiled to Lua using TypeScriptToLua. Since the input is still valid TypeScript it can also still be compiled to Javascript with the default TypeScript workflow. +# Feature support + +| Feature | Lua 5.1 | Lua 5.2 | Lua 5.3 | LuaJIT | +| ------------------- | :-----: | :-----: | :-----: | :----: | +| (Everything else) | ✔️ | ✔️ | ✔️ | ✔️ | +| Bitwise operators | ❌ | ✔️ | ✔️ | ✔️ | +| Switch statement | ❌ | ✔️ | ✔️ | ✔️ | +| `continue` | ❌ | ✔️ | ✔️ | ✔️ | +| `Promise` | ❌ | ❌ | ❌ | ❌ | +| `async` / `await` | ❌ | ❌ | ❌ | ❌ | +| Regular Expressions | ❌ | ❌ | ❌ | ❌ | +| Optional Chaining | ❌ | ❌ | ❌ | ❌ | +| Nullish Coalescing | ❌ | ❌ | ❌ | ❌ | + +# Differences from JavaScript + This project aims for both compilation results to have the same behavior as much as possible, but not at all costs. Since TypeScript is based on JavaScript it also inherited some of the quirks in JavaScript that are not present in Lua. This is where behavior between Lua and JavaScript compilation targets diverge. TypeScriptToLua aims to keep identical behavior as long as **sane** TypeScript is used: if JavaScript-specific quirks are used behavior might differ. Below are some of the cases where resulting Lua intentionally behaves different from compiled JS. -# [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) +## [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) -JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. Therefore there is also no difference between `==` and `===` when compiling to Lua, all comparisons are strict (`===`). +JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. | TypeScript | _JavaScript behavior_ | _Lua behavior_ | | ----------------- | --------------------- | -------------- | @@ -21,11 +36,15 @@ JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua a | `0` | `false` | ⚠️`true` | | (Everything else) | `true` | `true` | -# List Length +## [Loose equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using) + +TypeScriptToLua makes no difference between `==` and `===` when compiling to Lua, treating all comparisons as strict (`===`). -List length is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between Javascript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`. +## Array Length -## Examples: +`Array.prototype.length` is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between JavaScript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`. + +### Examples: **Safe (no difference):** @@ -51,8 +70,12 @@ myList[4] = 5; // myList.length == 3 (5 in JavaScript) ``` -# Key Iteration Order +## Key Iteration Order Even though iterating over object keys with `for ... in` does not guarantee order in either JavaScript or Lua. Therefore, the iteration order in JavaScript is likely different from the order in Lua. **Note:** If a specific order is required, it is better to use ordered collections like lists instead. + +## Iterating an array with `for ... in` + +Not allowed. diff --git a/docs/limitations.md b/docs/limitations.md deleted file mode 100644 index b98675a9..00000000 --- a/docs/limitations.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Limitations ---- - -There are certain features of Typescript, that are either not allowed or only partially supported. The following is a list of limitations. - -## String functions - -Not all string functions are supported. The supported functions are: - -- indexOf -- substring -- replace [[1](#stringreplace)] -- charAt -- split -- toLowerCase -- toUpperCase - -### string.replace - -Regular expressions in calls to `string.replace` are not supported. You can use Lua's Pattern strings (https://www.lua.org/pil/20.2.html) instead. - -## Array functions - -Not all array functions are supported. The supported functions are: - -- push -- forEach -- map -- filter -- some -- every -- slice -- splice -- join -- indexOf - -## Bit Operations - -Bit operations require Lua version `> 5.1` - -For `JIT` the LuaBitOp module (http://bitop.luajit.org) is required. - -## Try/Catch & Throw - -Basic support exists, but you are only allowed to throw string literals. - -## Iterating an array with `for ... in` - -Not allowed. diff --git a/docs/supported-lua-versions.md b/docs/supported-lua-versions.md deleted file mode 100644 index 30b7cb36..00000000 --- a/docs/supported-lua-versions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Supported Lua Versions ---- - -We currently aim to support all 5.x versions of Lua, aswell as Lua JIT. diff --git a/sidebars.json b/sidebars.json index fd09da2f..b92e1370 100644 --- a/sidebars.json +++ b/sidebars.json @@ -2,12 +2,10 @@ "docs": [ "getting-started", "configuration", - "differences-between-lua-and-javascript", "writing-declarations", "compiler-annotations", "functions-and-the-self-parameter", - "supported-lua-versions", - "limitations", + "caveats", "editor-support", { "type": "category", From fc26086413b8116130d4f550f030bc728b51763e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 15 Mar 2020 16:46:30 +0000 Subject: [PATCH 10/23] Add `API/Plugins` page --- docs/api/plugins.md | 93 +++++++++++++++++++++++++++++++++++++++++++++ sidebars.json | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 docs/api/plugins.md diff --git a/docs/api/plugins.md b/docs/api/plugins.md new file mode 100644 index 00000000..ed7bcc01 --- /dev/null +++ b/docs/api/plugins.md @@ -0,0 +1,93 @@ +--- +title: Plugins +--- + +TypeScriptToLua allows to customize transpilation behavior using plugins. + +To add a plugin you have to add it under `tstl.luaPlugins` option in the [configuration file](../configuration.md). + +Example: + +```json title=tsconfig.json +{ + "tstl": { + "luaPlugins": [ + // Plugin is a JavaScript module exporting an object + { "name": "./plugin1.js" }, + // TypeScriptToLua can load plugins written in TypeScript using `ts-node` + { "name": "./plugin2.ts" }, + // Plugins can be published to npm + { "name": "tstl-plugin-3" } + ] + } +} +``` + +## API + +### `visitors` + +Example: + +```ts +import * as ts from "typescript"; +import * as tstl from "typescript-to-lua"; + +const plugin: tstl.Plugin = { + // `visitors` is a record where keys are TypeScript node syntax kinds + visitors: { + // Visitor can be a function that returns Lua AST node + [ts.SyntaxKind.ReturnStatement]: () => tstl.createReturnStatement([tstl.createBooleanLiteral(true)]), + }, +}; + +export default plugin; +``` + +Example 2: + +```ts +import * as ts from "typescript"; +import * as tstl from "typescript-to-lua"; + +const plugin: tstl.Plugin = { + visitors: { + // Visit string literals, if original transformer returns a string literal, change the string to "bar" instead + [ts.SyntaxKind.StringLiteral]: (node, context) => { + // `context` exposes `superTransform*` methods, that can be used to call either the visitor provided by previous + // plugin, or a standard TypeScriptToLua visitor + const result = context.superTransformExpression(node); + + // Standard visitor for ts.StringLiteral always returns tstl.StringLiteral node + if (tstl.isStringLiteral(result)) { + result.value = "bar"; + } + + return result; + }, + }, +}; + +export default plugin; +``` + +### `printer` + +Example: + +```ts +import * as tstl from "typescript-to-lua"; + +const plugin: tstl.Plugin = { + // Printer is a function that can be used to override standard Lua AST printer + // It receives some information about the file and the transformed Lua AST + printer: (program, emitHost, fileName, ...args) => { + // You can get original printer result by constructing `LuaPrinter` and calling `print` method + const result = new tstl.LuaPrinter(program.getCompilerOptions(), emitHost, fileName).print(...args); + result.code = `-- Plugin\n${result.code}`; + return result; + }, +}; + +export default plugin; +``` diff --git a/sidebars.json b/sidebars.json index b92e1370..5a078038 100644 --- a/sidebars.json +++ b/sidebars.json @@ -10,7 +10,7 @@ { "type": "category", "label": "API", - "items": ["api/overview", "api/transformer", "api/printer"] + "items": ["api/overview", "api/transformer", "api/printer", "api/plugins"] }, { "type": "category", From f779bc43a00ad5cccb5b9abb40378199354d1a78 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 02:08:41 +0000 Subject: [PATCH 11/23] Rewrite `Getting Started` page with focus on project install --- docs/compiler-annotations.md | 6 ++--- docs/getting-started.md | 48 +++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/compiler-annotations.md b/docs/compiler-annotations.md index 5df3f418..5f6c151c 100644 --- a/docs/compiler-annotations.md +++ b/docs/compiler-annotations.md @@ -4,7 +4,7 @@ title: Compiler Annotations import { SideBySide } from "@site/src/components/SideBySide"; -To improve translation and compatibility to different Lua interfaces, the TypscriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax. +To improve translation and compatibility to different Lua interfaces, the TypeScriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax. ## @compileMembersOnly @@ -179,7 +179,7 @@ function MyBaseClass.myFunction(self) end **Target elements:** `declare function` -Denotes a function declaration is a Lua numerical iterator. When used in a Typescript `for...of` loop, the resulting Lua will use a numerical for loop. +Denotes a function declaration is a Lua numerical iterator. When used in a TypeScript `for...of` loop, the resulting Lua will use a numerical for loop. The function should not be a real function and an error will be thrown if it is used in any other way. @@ -207,7 +207,7 @@ for i = 10, 1, -1 do end **Target elements:** `(declare) interface` -Denotes a type is a Lua iterator. When an object of a type with this annotation is used in a for...of statement, it will transpile directly as a lua iterator in a for...in statement, instead of being treated as a Typescript iterable. Typically, this is used on an interface that extends `Iterable` or `Array` so that Typescript will allow it to be used in a for...of statement. +Denotes a type is a Lua iterator. When an object of a type with this annotation is used in a for...of statement, it will transpile directly as a lua iterator in a for...in statement, instead of being treated as a TypeScript iterable. Typically, this is used on an interface that extends `Iterable` or `Array` so that TypeScript will allow it to be used in a for...of statement. **Example** diff --git a/docs/getting-started.md b/docs/getting-started.md index 667f7494..3a6b082e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,29 +2,28 @@ title: Getting Started --- -This is a quick introduction into project setup and our CLI. -For a TypeScript quick start please read: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html +This is a quick introduction into project setup and our CLI. For a TypeScript quick start please read: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html ## Installation -NPM users can run: +TypeScriptToLua is built using [Node.js](https://nodejs.org/) and distributed via [npm](https://www.npmjs.com/). To install it, you need to create a `package.json` file in the root of your project, containing at least `{}`. Then you can use this command to add the latest version of TypeScriptToLua to your project: ```bash -npm install -g typescript-to-lua +npm install -D typescript-to-lua ``` -## Project Setup +## Project setup -We use the same configuration file that the vanilla TypeScript compiler `tsc` uses. -This file is called `tsconfig.json` and should be located in your projects root. +TypeScriptToLua shares the configuration format with vanilla TypeScript. This file is called `tsconfig.json` and should be located in your project's root. -Example: +Basic recommended configuration: ```json title=tsconfig.json { "compilerOptions": { "target": "esnext", "lib": ["esnext"], + "types": [], "strict": true }, "tstl": { @@ -37,24 +36,29 @@ Check out [Configuration](configuration.md) page for more information. ## Building your project -Our command line interface is called `tstl` and it works almost exactly as TypeScript's `tsc`, you can pass `tsc` options to `tstl`. +Our command line interface is called `tstl` and it works almost exactly as TypeScript's `tsc`. -Example: +> Note: Even though it's possible to install `tstl` globally, it is considered a bad practice. For for testing purposes you can run it with `npx typescript-to-lua`. -```bash -tstl -p pathToYour/tsconfig.json --luaTarget JIT --strict false -``` - -This command will build your project, overriding some options set in `tsconfig.json` (first example). +Since `tstl` is installed locally to your project, you cannot run it as a bare command in your terminal, so it's recommended to use it with [npm scripts](https://docs.npmjs.com/misc/scripts). -## Compiling files directly - -Example: - -```bash -tstl --luaTarget 5.1 --strict true script.ts +```json title=package.json +{ + "private": true, + "scripts": { + "build": "tstl", + "dev": "tstl --watch" + }, + "devDependencies": { + "typescript-to-lua": "..." + } +} ``` ```bash -tstl --luaTarget 5.1 --strict true script1.ts someDir/script2.ts +# Build +npm run build + +# Build and watch for changes +npm run dev ``` From 75e18859fb9c03eefc87f4c5cc66fa53e1d5abec Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 04:31:10 +0000 Subject: [PATCH 12/23] Remove `Project Design Goals` page --- docs/contributors/design-goals.md | 25 ------------------------- sidebars.json | 5 ----- 2 files changed, 30 deletions(-) delete mode 100644 docs/contributors/design-goals.md diff --git a/docs/contributors/design-goals.md b/docs/contributors/design-goals.md deleted file mode 100644 index b93c772a..00000000 --- a/docs/contributors/design-goals.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Project Design Goals ---- - -The design goals of this project can be summarized in the following sentence: - -> TypeScriptToLua aims to transpile valid TypeScript and declarations to working Lua for any Lua version and platform API. - -Ofcourse there are several nuances to be taken into account here, as will be discussed on the rest of this page. - -## Declarations First - -The main goal of this project is for it to be applicable to any Lua environment and API. This means the process of declaring platform APIs and existing Lua functionality are central in the design of the transpiler. As a result of this, TypeScriptToLua supports several [Compiler Annotations](../compiler-annotations.md) that affect the resulting Lua to better match API declarations. - -## Functional equivalence Lua/JavaScript - -Code written in TypeScript can be transpiled into both JavaScript and Lua. It would therefore be reasonable to expect that two programs with the same source are functionally equivalent: this is **NOT** the case. - -We aim to keep the functionality between transpiled JavaScript and Lua equivalent as much as possible. However, since JavaScript and Lua are fundamentally differences, equivalence of functionality can not be guaranteed. See [Differences between Lua and JavaScript](../differences-between-lua-and-javascript.md) for more details. - -We will try to stay functionally equivalent as much as possible, but not at all costs. If the workaround needed to support equivalent JavaScript functionality is too complex or flawed, we may accept slightly different functionality in Lua compared to JavaScript. A list of such limitations can be found here: [Limitations](../limitations.md). - -## Optimization strategy - -Since this project aims to be usable in any Lua environment, it can not optimize in any direction. We also value readable (and understandable) Lua output. We obviously aim for the best performance, but balancing all of the previously mentioned concerns. diff --git a/sidebars.json b/sidebars.json index 5a078038..6dc556b0 100644 --- a/sidebars.json +++ b/sidebars.json @@ -11,11 +11,6 @@ "type": "category", "label": "API", "items": ["api/overview", "api/transformer", "api/printer", "api/plugins"] - }, - { - "type": "category", - "label": "Contributors", - "items": ["contributors/design-goals"] } ] } From 27d9559016870ca7332cd13caf31f88dba9a41bc Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 20:06:17 +0000 Subject: [PATCH 13/23] Add `Type-directed emit` section to `Caveats` page --- docs/caveats.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/caveats.md b/docs/caveats.md index d3fdcb30..1fa79a2a 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -22,6 +22,10 @@ This project aims for both compilation results to have the same behavior as much Below are some of the cases where resulting Lua intentionally behaves different from compiled JS. +## Type-directed emit + +One of TypeScript's [design goals](https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals) is **not** using type information to affect program runtime behavior. Though this has many advantages (such as gradual typing), TypeScriptToLua uses type information extensively. This allows us to emit a much more optimized, portable, and correct Lua code. + ## [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. From d7ad84db2a4a2cb1887178a6317bf1e5de89aa41 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 21:02:14 +0000 Subject: [PATCH 14/23] Update API documentation --- docs/api/overview.md | 68 ++++----------- docs/api/plugins.md | 18 ++-- docs/api/printer.md | 122 ++++++++++++--------------- docs/api/transformer.md | 177 ---------------------------------------- docs/configuration.md | 19 ++++- sidebars.json | 2 +- 6 files changed, 96 insertions(+), 310 deletions(-) delete mode 100644 docs/api/transformer.md diff --git a/docs/api/overview.md b/docs/api/overview.md index 95ddae06..1cc35648 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -2,11 +2,9 @@ title: Overview --- -TypeScriptToLua provides a high-level and a low-level API. The high-level API can be used to invoke basic transpiler operations. The low-level API can be used to extend and override default transpiler behavior, to customize it to your specific environment. - ## High-level API -The high level API allows you to simply invoke several common transpiler operations from code. +The high level API allows you to simply invoke several common transpiler operations using well-known language primitives, handling usage of TypeScript API for you. ### TranspileString @@ -92,65 +90,35 @@ console.log(result.transpiledFiles); ## Low-level API -The low-level TypeScriptToLua API allows for extending or modifying of the default tstl transpilation process. For this to make sense it is important to know the process can broadly be split into **two phases: transforming and printing**. The first step of each transpilation is to **transform** the TypeScript AST of each file to a Lua AST. The next step is to **print** the resulting Lua AST to a string. TypeScriptToLua therefore implements a **LuaTransformer** and **LuaPrinter**. These two classes can be modified using the low-level API. +On the contrast with high-level API, low-level API requires you to to manage TypeScript project yourself. See [Using the Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) page for the introduction to TypeScript API. ### Transpile -The low-level API consists of only one function: `transpile`. It takes a TypeScript program, optional source files, and optional custom transformer and printer arguments, allowing you to override their default behavior first. - -More information on extending the transformer and printer can be found here: - -- [Custom LuaTransformer API](transformer.md) -- [Custom LuaPrinter API](printer.md) - **Arguments:** - program: ts.Program - The TypeScript program to transpile (note: unlike the high-level API, compilerOptions is part of the program and cannot be supplied separately). -- _[Optional]_ sourceFiles: ts.SourceFile[] - A collection of sourcefiles to transpile, `program.getSourceFiles()` by default. -- _[Optional]_ customTransformers: ts.CustomTransformers - Custom TypeScript transformers to apply before transpiling. -- _[Optional]_ transformer: tstl.LuaTransformer - If provided, this transformer is used instead of the default tstl transformer. -- _[Optional]_ printer: tstl.LuaPrinter - If provided, this printer is used instead of the default tstl printer. +- _[Optional]_ sourceFiles: ts.SourceFile[] - A collection of `SourceFile`s to transpile, `program.getSourceFiles()` by default. +- _[Optional]_ customTransformers: ts.CustomTransformers - List of extra [TypeScript transformers](../configuration.md#transformers). +- _[Optional]_ plugins: tstl.Plugin[] - List of [TypeScriptToLua plugins](plugins.md). - _[Optional]_ emitHost: tstl.EmitHost - Provides the methods for reading/writing files, useful in cases where you need something other than regular reading from disk. Defaults to `ts.sys`. **Example:** -This example shows using the low-level API to override how array literals are transpiled. By default, array literals are transformed as follows: `(TS)[1, 2, 3] -> (Lua){1, 2, 3}`. This example extends and overrides the default transformer to instead transform like this: `(TS)[1, 2, 3] -> (Lua){1, 2, 3, n=3}`. - ```ts -const options: tstl.CompilerOptions = { luaTarget: tstl.LuaTarget.Lua53 }; -const program = ts.createProgram({ rootNames: ["file1.ts", "file2.ts"], options }); - -class CustomTransformer extends tstl.LuaTransformer { - public transformArrayLiteral(expression: ts.ArrayLiteralExpression): tstl.ExpressionVisitResult { - // Call the original transformArrayLiteral first, to get the default result. - // You could also skip this and create your own table expression with tstl.createTableExpression() - const result = super.transformArrayLiteral(expression) as tstl.TableExpression; - - // Create the 'n = ' node - const nIdentifier = tstl.createIdentifier("n"); - const nValue = tstl.createNumericLiteral(expression.elements.length); - const tableField = tstl.createTableFieldExpression(nValue, nIdentifier); - - // Add the extra table field we created to the default transformation result - if (result.fields === undefined) { - result.fields = []; - } - result.fields.push(tableField); - - return result; - } +const reportDiagnostic = tstl.createDiagnosticReporter(true); +const configFileName = path.resolve(__dirname, "tsconfig.json"); +const parsedCommandLine = tstl.parseConfigFileWithSystem(configFileName); +if (parsedCommandLine.errors.length > 0) { + parsedCommandLine.errors.forEach(reportDiagnostic); + return; } -const transformer = new CustomTransformer(program); -const printer = new tstl.LuaPrinter(options); +const program = ts.createProgram(parsedCommandLine.fileNames, parsedCommandLine.options); +const { transpiledFiles, diagnostics: transpileDiagnostics } = tstl.transpile({ program }); -const result = tstl.transpile({ - program, - transformer, - printer, -}); -console.log(result.diagnostics); -console.log(result.transpiledFiles); -// Emit result -console.log(tstl.emitTranspiledFiles(options, result5.transpiledFiles)); +const emitResult = tstl.emitTranspiledFiles(options, transpiledFiles); +emitResult.forEach(({ name, text }) => ts.sys.writeFile(name, text)); + +const diagnostics = ts.sortAndDeduplicateDiagnostics([...ts.getPreEmitDiagnostics(program), ...transpileDiagnostics]); +diagnostics.forEach(reportDiagnostic); ``` diff --git a/docs/api/plugins.md b/docs/api/plugins.md index ed7bcc01..fabaa2f7 100644 --- a/docs/api/plugins.md +++ b/docs/api/plugins.md @@ -2,7 +2,7 @@ title: Plugins --- -TypeScriptToLua allows to customize transpilation behavior using plugins. +TypeScriptToLua supports plugins - an interface that allows to customize transpilation behavior. To add a plugin you have to add it under `tstl.luaPlugins` option in the [configuration file](../configuration.md). @@ -27,6 +27,8 @@ Example: ### `visitors` +Internally, to process [Abstract Syntax Tree](https://basarat.gitbook.io/typescript/overview/ast) of a TypeScript program, TypeScriptToLua implements the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern). Visitor is a function, called with a processed node and transformation context, and returning a Lua AST node. Plugins can inject their own visitors using `visitors` property, overriding standard transformation behavior. + Example: ```ts @@ -73,20 +75,18 @@ export default plugin; ### `printer` +Printer is a function that overrides standard implementation of Lua AST printer. It receives some information about the file and transformed Lua AST. See [Printer](printer.md) page for more information. + Example: ```ts import * as tstl from "typescript-to-lua"; +class CustomLuaPrinter extends tstl.LuaPrinter {} + const plugin: tstl.Plugin = { - // Printer is a function that can be used to override standard Lua AST printer - // It receives some information about the file and the transformed Lua AST - printer: (program, emitHost, fileName, ...args) => { - // You can get original printer result by constructing `LuaPrinter` and calling `print` method - const result = new tstl.LuaPrinter(program.getCompilerOptions(), emitHost, fileName).print(...args); - result.code = `-- Plugin\n${result.code}`; - return result; - }, + printer: (program, emitHost, fileName, block, luaLibFeatures) => + new CustomLuaPrinter(program.getCompilerOptions(), emitHost, fileName).print(block, luaLibFeatures), }; export default plugin; diff --git a/docs/api/printer.md b/docs/api/printer.md index c8b8e7bf..f6c4ee65 100644 --- a/docs/api/printer.md +++ b/docs/api/printer.md @@ -1,81 +1,61 @@ --- -title: LuaPrinter +title: Printer --- -The [LuaPrinter](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/src/LuaPrinter.ts) class takes Lua AST and prints it to a string (with source map). Like the LuaTransformer, the printer implements the visitor pattern. All methods visit nodes in the AST to print them to a `SourceNode`, this will automatically produce correct mappings in the resulting source map. - -## Visitor Pattern - -Like the LuaTransformer, the LuaPrinter class also implements a visitor pattern. For more explanation see the [visitor pattern explanation on the LuaTransformer page](transformer.md#visitor-pattern) +The [LuaPrinter](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/src/LuaPrinter.ts) class takes Lua AST and prints it to a string (with source map). The printer implements the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern). All methods visit nodes in the AST to print them to a [`SourceNode`](https://github.com/mozilla/source-map#sourcenode), this will automatically produce correct mappings in the resulting source map. ## API Reference -This is a list of all public overridable methods in the default TypeScriptToLua printer: - ```ts -class LuaPrinter { - public printStatement(statement: tstl.Statement): SourceNode; - - public printDoStatement(statement: tstl.DoStatement): SourceNode; - - public printVariableDeclarationStatement(statement: tstl.VariableDeclarationStatement): SourceNode; - - public printVariableAssignmentStatement(statement: tstl.AssignmentStatement): SourceNode; - - public printIfStatement(statement: tstl.IfStatement): SourceNode; - - public printWhileStatement(statement: tstl.WhileStatement): SourceNode; - - public printRepeatStatement(statement: tstl.RepeatStatement): SourceNode; - - public printForStatement(statement: tstl.ForStatement): SourceNode; - - public printForInStatement(statement: tstl.ForInStatement): SourceNode; - - public printGotoStatement(statement: tstl.GotoStatement): SourceNode; - - public printLabelStatement(statement: tstl.LabelStatement): SourceNode; - - public printReturnStatement(statement: tstl.ReturnStatement): SourceNode; - - public printBreakStatement(statement: tstl.BreakStatement): SourceNode; - - public printExpressionStatement(statement: tstl.ExpressionStatement): SourceNode; - - public printExpression(expression: tstl.Expression): SourceNode; - - public printStringLiteral(expression: tstl.StringLiteral): SourceNode; - - public printNumericLiteral(expression: tstl.NumericLiteral): SourceNode; - - public printNilLiteral(expression: tstl.NilLiteral): SourceNode; - - public printDotsLiteral(expression: tstl.DotsLiteral): SourceNode; - - public printBooleanLiteral(expression: tstl.BooleanLiteral): SourceNode; - - public printFunctionExpression(expression: tstl.FunctionExpression): SourceNode; - - public printFunctionDefinition(statement: tstl.FunctionDefinition): SourceNode; - - public printTableFieldExpression(expression: tstl.TableFieldExpression): SourceNode; - - public printTableExpression(expression: tstl.TableExpression): SourceNode; - - public printUnaryExpression(expression: tstl.UnaryExpression): SourceNode; - - public printBinaryExpression(expression: tstl.BinaryExpression): SourceNode; - - public printParenthesizedExpression(expression: tstl.ParenthesizedExpression): SourceNode; - - public printCallExpression(expression: tstl.CallExpression): SourceNode; - - public printMethodCallExpression(expression: tstl.MethodCallExpression): SourceNode; - - public printIdentifier(expression: tstl.Identifier): SourceNode; - - public printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode; +interface PrintResult { + code: string; + sourceMap: string; + sourceMapNode: SourceNode; +} - public printOperator(kind: tstl.Operator): SourceNode; +class LuaPrinter { + constructor(options: CompilerOptions, emitHost: EmitHost, fileName: string); + public print(block: lua.Block, luaLibFeatures: Set): PrintResult; + public printStatement(statement: lua.Statement): SourceNode; + public printDoStatement(statement: lua.DoStatement): SourceNode; + public printVariableDeclarationStatement(statement: lua.VariableDeclarationStatement): SourceNode; + public printVariableAssignmentStatement(statement: lua.AssignmentStatement): SourceNode; + public printIfStatement(statement: lua.IfStatement, isElseIf?: boolean): SourceNode; + public printWhileStatement(statement: lua.WhileStatement): SourceNode; + public printRepeatStatement(statement: lua.RepeatStatement): SourceNode; + public printForStatement(statement: lua.ForStatement): SourceNode; + public printForInStatement(statement: lua.ForInStatement): SourceNode; + public printGotoStatement(statement: lua.GotoStatement): SourceNode; + public printLabelStatement(statement: lua.LabelStatement): SourceNode; + public printReturnStatement(statement: lua.ReturnStatement): SourceNode; + public printBreakStatement(statement: lua.BreakStatement): SourceNode; + public printExpressionStatement(statement: lua.ExpressionStatement): SourceNode; + public printExpression(expression: lua.Expression): SourceNode; + public printStringLiteral(expression: lua.StringLiteral): SourceNode; + public printNumericLiteral(expression: lua.NumericLiteral): SourceNode; + public printNilLiteral(expression: lua.NilLiteral): SourceNode; + public printDotsLiteral(expression: lua.DotsLiteral): SourceNode; + public printBooleanLiteral(expression: lua.BooleanLiteral): SourceNode; + public printFunctionExpression(expression: lua.FunctionExpression): SourceNode; + public printFunctionDefinition(statement: lua.FunctionDefinition): SourceNode; + public printTableFieldExpression(expression: lua.TableFieldExpression): SourceNode; + public printTableExpression(expression: lua.TableExpression): SourceNode; + public printUnaryExpression(expression: lua.UnaryExpression): SourceNode; + public printBinaryExpression(expression: lua.BinaryExpression): SourceNode; + public printCallExpression(expression: lua.CallExpression): SourceNode; + public printMethodCallExpression(expression: lua.MethodCallExpression): SourceNode; + public printIdentifier(expression: lua.Identifier): SourceNode; + public printTableIndexExpression(expression: lua.TableIndexExpression): SourceNode; + public printOperator(kind: lua.Operator): SourceNode; + protected pushIndent(): void; + protected popIndent(): void; + protected indent(input?: SourceChunk): SourceChunk; + protected createSourceNode(node: lua.Node, chunks: SourceChunk | SourceChunk[], name?: string): SourceNode; + protected concatNodes(...chunks: SourceChunk[]): SourceNode; + protected printBlock(block: lua.Block): SourceNode; + protected printStatementArray(statements: lua.Statement[]): SourceChunk[]; + protected isStatementEmpty(statement: lua.Statement): boolean; + protected joinChunks(separator: string, chunks: SourceChunk[]): SourceChunk[]; + protected printExpressionList(expressions: lua.Expression[]): SourceChunk[]; } ``` diff --git a/docs/api/transformer.md b/docs/api/transformer.md deleted file mode 100644 index 72377a9f..00000000 --- a/docs/api/transformer.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: LuaTransformer ---- - -If you need some Lua transformations for a very specific environment, it is possible to implement these using custom transformers. Writing a custom transformer involves extending the [default TypeScriptToLua LuaTransformer class](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/src/LuaTransformer.ts) and overriding its functionality. - -### Visitor Pattern - -The LuaTransformer class implements the visitor pattern. Therefore to override specific transformation behavior for specific TypeScript statements, it is only necessary to override that specific statement's transform method. - -### Example - -Assume we want to add to add a `array.n` property to all array table literals, we need to override: - -```ts -transformArrayLiteral(expression: ts.ArrayLiteralExpression): ExpressionVisitResult -``` - -Since the rest of the functionality should be the same, we can simply copy paste the default implementation and add our extra parameter. - -The resulting transformer would look something like: - -```ts -class CustomTransformer extends tstl.LuaTransformer { - public transformArrayLiteral(node: ts.ArrayLiteralExpression): ExpressionVisitResult { - const values: tstl.TableFieldExpression[] = []; - - node.elements.forEach(e => { - const element = this.transformExpression(e); // We can just call the default transformations - values.push(tstl.createTableFieldExpression(element, undefined, e)); - }); - - // Custom behavior: Add n= to lua array table. - const nIdentifier = tstl.createStringLiteral("n"); - const nValue = tstl.createNumericLiteral(node.elements.length); - values.push(tstl.createTableFieldExpression(nValue, nIdentifier)); - // End of custom behavior - - return tstl.createTableExpression(values, node); - } -} -``` - -## API Reference - -This is a list of all public overridable methods in the default TypeScriptToLua tranformer: - - -```ts -class LuaTransformer { - public transformSourceFile(sourceFile: ts.SourceFile): [tstl.Block, Set]; - - public transformStatement(statement: ts.Statement): StatementVisitResult; - - public transformBlock(block: ts.Block): tstl.Block; - - public transformExportDeclaration(declaration: ts.ExportDeclaration): StatementVisitResult; - - public transformImportDeclaration(declaration: ts.ImportDeclaration): StatementVisitResult; - - public transformClassDeclaration(declaration: ts.ClassLikeDeclaration, nameOverride?: tstl.Identifier): StatementVisitResult; - - public transformGetAccessorDeclaration(declaration: ts.GetAccessorDeclaration, className: tstl.Identifier): StatementVisitResult; - - public transformSetAccessorDeclaration(declaration: ts.GetAccessorDeclaration, className: tstl.Identifier): StatementVisitResult; - - public transformMethodDeclaration(declaration: ts.MethodDeclaration, className: tstl.Identifier, noPrototype: boolean): StatementVisitResult; - - public transformBindingPattern(pattern: ts.BindingPattern, table: tstl.Identifier,propertyStack: ts.PropertyName): StatementVisitResult; - - public transformModuleDeclaration(declaration: ts.ModuleDeclaration): StatementVisitResult; - - public transformEnumDeclaration(declaration: ts.EnumDeclaration): StatementVisitResult; - - public transformFunctionDeclaration(declaration: ts.FunctionDeclaration): StatementVisitResult; - - public transformTypeAliasDeclaration(declaration: ts.TypeAliasDeclaration): StatementVisitResult; - - public transformInterfaceDeclaration(declaration: ts.InterfaceDeclaration): StatementVisitResult; - - public transformVariableDeclaration(declaration: ts.VariableDeclaration): StatementVisitResult; - - public transformVariableStatement(statement: ts.VariableStatement): StatementVisitResult; - - public transformExpressionStatement(statement: ts.ExpressionStatement | ts.Expression): StatementVisitResult; - - public transformReturnStatement(statement: ts.ReturnStatement): StatementVisitResult; - - public transformIfStatement(statement: ts.IfStatement): StatementVisitResult; - - public transformWhileStatement(statement: ts.WhileStatement): StatementVisitResult; - - public transformDoStatement(statement: ts.DoStatement): StatementVisitResult; - - public transformForStatement(statement: ts.ForStatement): StatementVisitResult; - - public transformForOfStatement(statement: ts.ForOfStatement): StatementVisitResult; - - public transformForInStatement(statement: ts.ForInStatement): StatementVisitResult; - - public transformSwitchStatement(statement: ts.SwitchStatement): StatementVisitResult; - - public transformBreakStatement(statement: ts.BreakStatement): StatementVisitResult; - - public transformTryStatement(statement: ts.TryStatement): StatementVisitResult; - - public transformThrowStatement(statement: ts.ThrowStatement): StatementVisitResult; - - public transformContinueStatement(statement: ts.ContinueStatement): StatementVisitResult; - - public transformEmptyStatement(statement: ts.EmptyStatement): StatementVisitResult; - - // Expressions - - public transformExpression(expression: ts.Expression): ExpressionVisitResult; - - public transformBinaryExpression(expression: ts.BinaryExpression): ExpressionVisitResult; - - public transformBinaryOperator(operator: ts.BinaryOperator, node: ts.Node): tstl.BinaryOperator; - - public transformClassExpression(expression: ts.ClassExpression): ExpressionVisitResult; - - public transformConditionalExpression(expression: ts.ConditionalExpression): ExpressionVisitResult; - - public transformPostfixUnaryExpression(expression: ts.PostfixUnaryExpression): ExpressionVisitResult; - - public transformPrefixUnaryExpression(expression: ts.PrefixUnaryExpression): ExpressionVisitResult; - - public transformArrayLiteral(expression: ts.ArrayLiteralExpression): ExpressionVisitResult; - - public transformObjectLiteral(expression: ts.ObjectLiteralExpression): ExpressionVisitResult; - - public transformDeleteExpression(expression: ts.DeleteExpression): ExpressionVisitResult; - - public transformFunctionExpresssion(expression: ts.FunctionExpression): ExpressionVisitResult; - - public transformNewExpression(expression: ts.NewExpression): ExpressionVisitResult; - - public transformParenthesizedExpression(expression: ts.ParenthesizedExpression): ExpressionVisitResult; - - public transformSuperKeyword(expression: ts.SuperExpression): ExpressionVisitResult; - - public transformCallExpression(expression: ts.CallExpression): ExpressionVisitResult; - - public transformPropertyAccessExpression(expression: ts.PropertyAccessExpression): ExpressionVisitResult; - - public transformElementAccessExpression(expression: ts.ElementAccessExpression): ExpressionVisitResult; - - public transformArrayBindingElement(expression: ts.ArrayBindingelement): ExpressionVisitResult; - - public transformAssertionExpression(expression: ts.AssertionExpression): ExpressionVisitResult; - - public transformTypeOfExpression(expression: ts.TypeOfExpression): ExpressionVisitResult; - - public transformSpreadElement(expression: ts.SpreadElement): ExpressionVisitResult; - - public transformStringLIteral(literal: ts.StringLiteralLike): ExpressionVisitResult; - - public transformNumericLiteral(literal: ts.NumericLiteral): ExpressionVisitResult; - - public transformTrueKeyword(keyword: ts.BooleanLiteral): ExpressionVisitResult; - - public transformFalseKeyword(keyword: ts.BooleanLiteral): ExpressionVisitResult; - - public transformNullOrUndefinedKeyword(keyword: ts.Node): ExpressionVisitResult; - - public transformThisKeyword(keyword: ts.ThisExpression): ExpressionVisitResult; - - public transformTemplateExpression(expression: ts.TemplateExpression): ExpressionVisitResult; - - public transformPropertyName(expression: ts.PropertyName): ExpressionVisitResult; - - public transformIdentifier(expression: ts.Identifier): tstl.Identifier; - - public transformYield(expression: ts.YieldExpression): ExpressionVisitResult; -} -``` diff --git a/docs/configuration.md b/docs/configuration.md index cdc98d54..97182a3a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -4,7 +4,7 @@ title: Configuration TypeScriptToLua uses the same configuration file as the vanilla TypeScript compiler, loading it from the `tsconfig.json` file using the same rules as `tsc`. -# Custom options +## Custom options To customize transpilation behavior we add a new group of options to the `tsconfig.json` file. All of these options should be placed in a `tstl` object. @@ -25,8 +25,9 @@ To customize transpilation behavior we add a new group of options to the `tsconf | `sourceMapTraceback` | `true`, `false` (default: `false`) | Overrides Lua's `debug.traceback` to apply sourcemaps to Lua stacktraces. This will make error messages point to your original TypeScript code instead of the generated Lua. | | `luaBundle` | File path (relative to the `tsconfig.json`) | Will bundle all output lua files into a single bundle file. Requires **luaBundleEntry** to be set! | | `luaBundleEntry` | File path (relative to the `tsconfig.json`) | This should be the name/path of the TS file in your project that will serve as entry point to the bundled code. | +| `luaPlugins` | `Array<{ name: string; import?: string }>` | List of [TypeScriptToLua plugins](plugins.md). | -# Standard options +## Standard options Most of the standard [TypeScript options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) work without any changes. Notable unsupported options are: @@ -41,3 +42,17 @@ Some options do not apply to TypeScriptToLua and are ignored: - `outFile` - use `luaBundle` instead. - `importHelpers`, `noEmitHelpers` - use `luaLibImport` instead. - `target`, `module` - it's only effect is limiting some features, so prefer to set it to `esnext`. If TypeScript requires you to specify different `module` type because you want to bundle your declarations with `outFile` and `declarations`, consider using [API Extractor](https://api-extractor.com/) instead. + +## Transformers + +Transformers is a powerful feature of TypeScript that allows you to modify behavior of your program during compilation. While TypeScript [currently](https://github.com/microsoft/TypeScript/issues/14419) does not provide a user-facing way to use transformers, TypeScriptToLua allows you to specify them in the configuration file, following [ttypescript](https://github.com/cevek/ttypescript#how-to-use) format. + +**Example:** + +```json title=tsconfig.json +{ + "compilerOptions": { + "plugins": [{ "transform": "dota-lua-types/transformer" }] + } +} +``` diff --git a/sidebars.json b/sidebars.json index 6dc556b0..516deced 100644 --- a/sidebars.json +++ b/sidebars.json @@ -10,7 +10,7 @@ { "type": "category", "label": "API", - "items": ["api/overview", "api/transformer", "api/printer", "api/plugins"] + "items": ["api/overview", "api/plugins", "api/printer"] } ] } From 101fedb6fd7e18f771bf5ad03b8a2698cec06629 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 22:16:47 +0000 Subject: [PATCH 15/23] Create `Advanced` category --- docs/{ => advanced}/compiler-annotations.md | 0 .../functions-and-the-self-parameter.md | 0 docs/{ => advanced}/writing-declarations.md | 0 sidebars.json | 12 +++++++++--- 4 files changed, 9 insertions(+), 3 deletions(-) rename docs/{ => advanced}/compiler-annotations.md (100%) rename docs/{ => advanced}/functions-and-the-self-parameter.md (100%) rename docs/{ => advanced}/writing-declarations.md (100%) diff --git a/docs/compiler-annotations.md b/docs/advanced/compiler-annotations.md similarity index 100% rename from docs/compiler-annotations.md rename to docs/advanced/compiler-annotations.md diff --git a/docs/functions-and-the-self-parameter.md b/docs/advanced/functions-and-the-self-parameter.md similarity index 100% rename from docs/functions-and-the-self-parameter.md rename to docs/advanced/functions-and-the-self-parameter.md diff --git a/docs/writing-declarations.md b/docs/advanced/writing-declarations.md similarity index 100% rename from docs/writing-declarations.md rename to docs/advanced/writing-declarations.md diff --git a/sidebars.json b/sidebars.json index 516deced..4db4962d 100644 --- a/sidebars.json +++ b/sidebars.json @@ -2,11 +2,17 @@ "docs": [ "getting-started", "configuration", - "writing-declarations", - "compiler-annotations", - "functions-and-the-self-parameter", "caveats", "editor-support", + { + "type": "category", + "label": "Advanced", + "items": [ + "advanced/writing-declarations", + "advanced/compiler-annotations", + "advanced/functions-and-the-self-parameter" + ] + }, { "type": "category", "label": "API", From ca342bfc4fe51d866e6fef46d6755207e4625467 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 20 Mar 2020 23:01:31 +0000 Subject: [PATCH 16/23] Don't use level 1 headings --- .../functions-and-the-self-parameter.md | 14 +++++++------- docs/caveats.md | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/advanced/functions-and-the-self-parameter.md b/docs/advanced/functions-and-the-self-parameter.md index 32bce6cc..d60f9a7d 100644 --- a/docs/advanced/functions-and-the-self-parameter.md +++ b/docs/advanced/functions-and-the-self-parameter.md @@ -4,7 +4,7 @@ title: Functions and the `self` Parameter import { SideBySide } from "@site/src/components/SideBySide"; -# Every Function Has a Context Parameter +## Every Function Has a Context Parameter In JavaScript and TypeScript, almost all functions have access to an implicit `this` parameter. In order to maintain compatibility with this, all Lua functions are generated with an extra initial context parameter. @@ -68,11 +68,11 @@ myLibFunction(nil, "foo")
-# Removing the Context Parameter +## Removing the Context Parameter When dealing with external library functions that don't expect this initial parameter, you will need to inform TypeScriptToLua. This can be done a few different ways. -## `this: void` +### `this: void` You can declare any function with `this: void` to prevent generation of this initial argument. @@ -139,7 +139,7 @@ takesCallback(function(arg) print(arg) end)
-## `@noSelf` +### `@noSelf` If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](compiler-annotations.md#noself) directive. @@ -181,13 +181,13 @@ MyNamespace:myFunction("foo")
-## `@noSelfInFile` +### `@noSelfInFile` If you want to specify that all functions in a file should have no context, you can use [`@noSelfInFile`](compiler-annotations.md#noselfinfile) at the top of the file. For more information on [`@noSelf`](compiler-annotations.md#noself) and [`@noSelfInFile`](compiler-annotations.md#noselfinfile), please refer to [Compiler Annotations](compiler-annotations). -# Assignment Errors +## Assignment Errors Functions that have a context parameter cannot be assigned to functions that do not, and vice-versa. A common case where this may occur is passing a callback to an api that expects a function that does not take an initial argument. @@ -218,7 +218,7 @@ takesCallback(function(arg) return myCallback(nil, arg) end) The reason this works is because TypeScriptToLua infers whether the arrow function should take a context parameter or not based on the type it's being assigned to. -## Overloads +### Overloads If a function is overloaded and the signatures differ in context type, you can not assign them: diff --git a/docs/caveats.md b/docs/caveats.md index 1fa79a2a..5aa5c41a 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -2,7 +2,7 @@ title: Caveats --- -# Feature support +## Feature support | Feature | Lua 5.1 | Lua 5.2 | Lua 5.3 | LuaJIT | | ------------------- | :-----: | :-----: | :-----: | :----: | @@ -16,17 +16,17 @@ title: Caveats | Optional Chaining | ❌ | ❌ | ❌ | ❌ | | Nullish Coalescing | ❌ | ❌ | ❌ | ❌ | -# Differences from JavaScript +## Differences from JavaScript This project aims for both compilation results to have the same behavior as much as possible, but not at all costs. Since TypeScript is based on JavaScript it also inherited some of the quirks in JavaScript that are not present in Lua. This is where behavior between Lua and JavaScript compilation targets diverge. TypeScriptToLua aims to keep identical behavior as long as **sane** TypeScript is used: if JavaScript-specific quirks are used behavior might differ. Below are some of the cases where resulting Lua intentionally behaves different from compiled JS. -## Type-directed emit +### Type-directed emit One of TypeScript's [design goals](https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals) is **not** using type information to affect program runtime behavior. Though this has many advantages (such as gradual typing), TypeScriptToLua uses type information extensively. This allows us to emit a much more optimized, portable, and correct Lua code. -## [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) +### [Boolean coercion](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua adheres to the Lua evaluations. @@ -40,15 +40,15 @@ JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua a | `0` | `false` | ⚠️`true` | | (Everything else) | `true` | `true` | -## [Loose equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using) +### [Loose equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using) TypeScriptToLua makes no difference between `==` and `===` when compiling to Lua, treating all comparisons as strict (`===`). -## Array Length +### Array Length `Array.prototype.length` is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between JavaScript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`. -### Examples: +**Examples:** **Safe (no difference):** @@ -74,12 +74,12 @@ myList[4] = 5; // myList.length == 3 (5 in JavaScript) ``` -## Key Iteration Order +### Key Iteration Order Even though iterating over object keys with `for ... in` does not guarantee order in either JavaScript or Lua. Therefore, the iteration order in JavaScript is likely different from the order in Lua. **Note:** If a specific order is required, it is better to use ordered collections like lists instead. -## Iterating an array with `for ... in` +### Iterating an array with `for ... in` Not allowed. From 24182d7077919a70eaff927ffc49ae33d18e44a3 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 21 Mar 2020 00:25:22 +0000 Subject: [PATCH 17/23] Add `Declarations` section to `Getting Started` page --- docs/getting-started.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 3a6b082e..299405a9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -62,3 +62,14 @@ npm run build # Build and watch for changes npm run dev ``` + +## Declarations + +The real power of this transpiler is usage together with good declarations for the Lua API provided. Some examples of Lua interface declarations can be found here: + +- [Lua Standard Library](https://github.com/TypeScriptToLua/lua-types) +- [Dota 2 Custom Games](https://github.com/ModDota/API/tree/master/declarations/server) ([template](https://github.com/ModDota/TypeScriptAddonTemplate)) +- [Defold Game Engine Scripting](https://github.com/dasannikov/DefoldTypeScript/blob/master/defold.d.ts) +- [LÖVE 2D Game Development](https://github.com/hazzard993/love-typescript-definitions) +- [World of Warcraft - Addon Development](https://github.com/wartoshika/wow-declarations) +- [World of Warcraft Classic - Addon Development](https://github.com/wartoshika/wow-classic-declarations) From 6fadcd009fbce28325f8a07654ab86694a0238a8 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 5 Apr 2020 16:37:29 +0000 Subject: [PATCH 18/23] Minor improvements --- .../functions-and-the-self-parameter.md | 2 +- docs/advanced/writing-declarations.md | 54 ++++++++++--------- docs/caveats.md | 36 +++++++++---- docs/configuration.md | 3 +- 4 files changed, 55 insertions(+), 40 deletions(-) diff --git a/docs/advanced/functions-and-the-self-parameter.md b/docs/advanced/functions-and-the-self-parameter.md index d60f9a7d..b0d139fc 100644 --- a/docs/advanced/functions-and-the-self-parameter.md +++ b/docs/advanced/functions-and-the-self-parameter.md @@ -141,7 +141,7 @@ takesCallback(function(arg) print(arg) end) ### `@noSelf` -If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](compiler-annotations.md#noself) directive. +If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the [`@noSelf`](compiler-annotations.md#noself) annotation. **Example** diff --git a/docs/advanced/writing-declarations.md b/docs/advanced/writing-declarations.md index f2f3cad0..471d4d01 100644 --- a/docs/advanced/writing-declarations.md +++ b/docs/advanced/writing-declarations.md @@ -141,13 +141,13 @@ declare namespace table { /** @noSelfInFile */ declare namespace table { - export function remove(this: void, table: object, index: number): any; + export function remove(table: object, index: number): any; } ``` > By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. -## Comments and Directives +## Comments and Annotations If you're using an editor that seeks out information about functions, variables, etc. It will likely find the file where what it is analyzing is defined and check out the comment above it. @@ -161,16 +161,16 @@ declare function print(...args: any[]);

Try out what this looks like in an editor

-TypeScript uses TSDoc for its comments. TSDoc allows you to also use markdown in your comments! This means pictures, links, tables, code syntax highlighting and more markdown features are available. These may display differently depending on the editor in use. +TypeScript uses [TSDoc](https://github.com/microsoft/tsdoc) for its comments. TSDoc allows you to also use markdown in your comments! This means pictures, links, tables, code syntax highlighting and more markdown features are available. These may display differently depending on the editor in use. Here are some commonly used TSDoc tags used in comments: -| Tag | Description | -| -------------------------------------- | ---------------------------------------------------- | -| `@param ` | Defines a parameter. e.g. A parameter for a function | -| `@return ` | Describes the return value of a function / method | +| Tag | Description | +| ----------------------------- | ---------------------------------------------------- | +| `@param ` | Defines a parameter. e.g. A parameter for a function | +| `@return ` | Describes the return value of a function / method | -TypeScriptToLua takes this further. Some "tags" change how the transpiler translates certain pieces of code. These are referred to as _Directives_. +TypeScriptToLua takes this further. Some "tags" change how the transpiler translates certain pieces of code. These are referred to as [annotations](compiler-annotations.md). As an example, `@tupleReturn` marks a function as something which returns multiple values instead of its array. @@ -195,7 +195,7 @@ let [c, d] = array(); // local c, d = unpack(array()) ``` -See [Compiler Annotations](compiler-annotations.md) page for more directive info. +See [Compiler Annotations](compiler-annotations.md) page for more information. ## Environmental Declarations @@ -312,7 +312,7 @@ p.tuple(); ### Ambient Modules -You may have to use the `@noResolution` directive to tell TypeScriptToLua to not try any path resolution methods when the specified module is imported. +You may have to use the `@noResolution` annotation to tell TypeScriptToLua to not try any path resolution methods when the specified module is imported. Module declarations need to be kept in _.d.ts_ files. @@ -336,19 +336,20 @@ declare module "number-of-the-day" { * Not very useful for TypeScript. It has no idea what is in here. * @noResolution */ -declare module "custommodule"; +declare module "custom-module"; ``` ```ts title=main.ts import { getimagewidth, getimageheight } from "image-size"; -import * as x from "contains_a_number"; -import * as custommodule from "custommodule"; +import * as x from "number-of-the-day"; +import * as customModule from "custom-module"; ``` ### Unions Unions can be used to tell TypeScript that a given type could be one of many other types. TypeScript can then pick up hints in the code to figure out what that type is at a given statement. + ```ts title=main.ts declare interface PingResponse { type: "ping"; @@ -369,15 +370,16 @@ response.timeTaken; switch (response.type) { case "ping": + // If the program arrives here, response: PingResponse return response.timeTaken; - // If the program arrives here, response: PingResponse case "message": + // If the program arrives here, response: MessageResponse return response.text; - // If the program arrives here, response: MessageResponse - case "disconnect": // Impossible + case "disconnect": + // Impossible default: - // Because of what Response is described as, TypeScript knows getting - // here is impossible. + // Because of what Response is described as, TypeScript knows getting + // here is impossible. } ``` @@ -416,27 +418,27 @@ The parent to these kinds of functions will need to be represented as a JSON obj ```ts // ❌ declare namespace table { - export function new: () => any; + export function new: () => any; } // ✔ declare let table: { - new: () => any; -} + new: () => any; +}; ``` ```ts // ❌ declare module "creator" { - export function new: () => any; + export function new: () => any; } // ✔ declare module "creator" { - let exports: { - new: () => any; - } - export = exports; + let exports: { + new: () => any; + }; + export = exports; } ``` diff --git a/docs/caveats.md b/docs/caveats.md index 5aa5c41a..b33286ee 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -4,17 +4,31 @@ title: Caveats ## Feature support -| Feature | Lua 5.1 | Lua 5.2 | Lua 5.3 | LuaJIT | -| ------------------- | :-----: | :-----: | :-----: | :----: | -| (Everything else) | ✔️ | ✔️ | ✔️ | ✔️ | -| Bitwise operators | ❌ | ✔️ | ✔️ | ✔️ | -| Switch statement | ❌ | ✔️ | ✔️ | ✔️ | -| `continue` | ❌ | ✔️ | ✔️ | ✔️ | -| `Promise` | ❌ | ❌ | ❌ | ❌ | -| `async` / `await` | ❌ | ❌ | ❌ | ❌ | -| Regular Expressions | ❌ | ❌ | ❌ | ❌ | -| Optional Chaining | ❌ | ❌ | ❌ | ❌ | -| Nullish Coalescing | ❌ | ❌ | ❌ | ❌ | +| Feature | Lua 5.1 | Lua 5.2 | Lua 5.3 | LuaJIT | +| --------------------- | :-----: | :-----: | :-----: | :----: | +| (Everything else) | ✔️ | ✔️ | ✔️ | ✔️ | +| [Bitwise operators] | ❌ | ✔️ | ✔️ | ✔️ | +| [Switch statement] | ❌ | ✔️ | ✔️ | ✔️ | +| [`continue`] | ❌ | ✔️ | ✔️ | ✔️ | +| [`Promise`] | ❌ | ❌ | ❌ | ❌ | +| [`async`] / [`await`] | ❌ | ❌ | ❌ | ❌ | +| [Regular Expressions] | ❌ | ❌ | ❌ | ❌ | +| [Optional Chaining] | ❌ | ❌ | ❌ | ❌ | +| [Nullish Coalescing] | ❌ | ❌ | ❌ | ❌ | +| [Private Fields] | ❌ | ❌ | ❌ | ❌ | +| [JSX] | ❌ | ❌ | ❌ | ❌ | + +[bitwise operators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators +[switch statement]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch +[`continue`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue +[`promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +[`async`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function +[`await`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await +[regular expressions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions +[optional chaining]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining +[nullish coalescing]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator +[private fields]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields#Private_fields +[jsx]: https://www.typescriptlang.org/docs/handbook/jsx.html ## Differences from JavaScript diff --git a/docs/configuration.md b/docs/configuration.md index 97182a3a..bac7f6fb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -35,13 +35,12 @@ Most of the standard [TypeScript options](https://www.typescriptlang.org/docs/ha - `incremental` - `emitDecoratorMetadata` - `esModuleInterop` -- `jsx` Some options do not apply to TypeScriptToLua and are ignored: - `outFile` - use `luaBundle` instead. - `importHelpers`, `noEmitHelpers` - use `luaLibImport` instead. -- `target`, `module` - it's only effect is limiting some features, so prefer to set it to `esnext`. If TypeScript requires you to specify different `module` type because you want to bundle your declarations with `outFile` and `declarations`, consider using [API Extractor](https://api-extractor.com/) instead. +- `target`, `module` - it's only effect is limiting some features, so prefer to set it to `esnext`. If TypeScript requires you to specify different `module` type because you want to bundle your declarations with `outFile`, consider using [API Extractor](https://api-extractor.com/) instead. ## Transformers From 13c88ee6b311ac46c1b5748d03aa4cf43051a34c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 5 Apr 2020 20:33:40 +0000 Subject: [PATCH 19/23] Use admonitions --- docs/advanced/writing-declarations.md | 16 ++++++++++------ docs/getting-started.md | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/advanced/writing-declarations.md b/docs/advanced/writing-declarations.md index 471d4d01..b6a0e977 100644 --- a/docs/advanced/writing-declarations.md +++ b/docs/advanced/writing-declarations.md @@ -10,15 +10,15 @@ If you need tips or help writing declarations, feel free to [join our Discord](h Declaration files end with the extension `.d.ts`. These contain pure ambient code. -> Ambient : relating to the immediate surroundings of something. - For TypeScriptToLua, these files should contain information that describes the target Lua environment. This means functions, modules, variables and other members of the target Lua environment are primarily described in these files. They don't contain code that you would execute. Similar to how you'd write an interface in some other languages. TypeScriptToLua doesn't output any information from these files either. -> Note: You can write ambient declarations inside `.ts` files as well. +:::note +You can write ambient declarations inside `.ts` files as well. +::: ## Declare Keyword @@ -50,7 +50,9 @@ declare function print(...args: any[]): void; print(_VERSION); // Editor and transpiler know what print and _VERSION are ``` -> Note: You can use _declare_ to write ambient declarations inside `.ts` files. +:::note +You can use _declare_ to write ambient declarations inside `.ts` files. +::: ## Export Keyword @@ -145,7 +147,7 @@ declare namespace table { } ``` -> By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. +By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. ## Comments and Annotations @@ -407,7 +409,9 @@ draw("solid", 0, 0, 16, 16); // Valid draw("rounded", 0, 0, 16, 16); // Invalid ``` -> This can apply to numbers as well +:::info +This can apply to numbers as well. +::: ### Keyword Workarounds diff --git a/docs/getting-started.md b/docs/getting-started.md index 299405a9..352e7a05 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -38,7 +38,9 @@ Check out [Configuration](configuration.md) page for more information. Our command line interface is called `tstl` and it works almost exactly as TypeScript's `tsc`. -> Note: Even though it's possible to install `tstl` globally, it is considered a bad practice. For for testing purposes you can run it with `npx typescript-to-lua`. +:::note +Even though it's possible to install `tstl` globally, it is considered a bad practice. For for testing purposes you can run it with `npx typescript-to-lua`. +::: Since `tstl` is installed locally to your project, you cannot run it as a bare command in your terminal, so it's recommended to use it with [npm scripts](https://docs.npmjs.com/misc/scripts). From c1790aa1bc1f6c695edbfd2ae56650800289dc22 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 7 Apr 2020 17:59:11 +0000 Subject: [PATCH 20/23] Improve title annotations on `Writing Declarations` page --- docs/advanced/writing-declarations.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/advanced/writing-declarations.md b/docs/advanced/writing-declarations.md index b6a0e977..188b805d 100644 --- a/docs/advanced/writing-declarations.md +++ b/docs/advanced/writing-declarations.md @@ -72,7 +72,7 @@ import { x } from "./lib"; If a namespace contains certain functions, `export` tells TypeScript that those functions can be accessed within the namespace. -```ts title=.d.ts +```ts title=table.d.ts declare namespace table { /** * @noSelf @@ -87,7 +87,7 @@ table.insert({}, 1); If a globally available module exists within the Lua environment. You can define what the module provides. -```ts title=.d.ts +```ts title=utf8.d.ts declare module "utf8" { /** * @noSelf @@ -96,7 +96,7 @@ declare module "utf8" { } ``` -```ts +```ts title=main.ts import * as utf8 from "utf8"; // equiv to `local utf8 = require("utf8"); utf8.codepoint(); ``` @@ -153,7 +153,7 @@ By doing this, the transpiler also figures out if it needs to use _:_ or _._ whe If you're using an editor that seeks out information about functions, variables, etc. It will likely find the file where what it is analyzing is defined and check out the comment above it. -```ts title=print.d.ts +```ts /** * When hovering over print, this description will be shown * @param args Stuff to print @@ -233,11 +233,11 @@ Some examples of declaration merging have been shown in the above examples. Some tables can use `__call` to make themselves callable. Busted (the Lua testing suite) does this to `assert`. -```ts title=.d.ts +```ts title=assert.d.ts +declare function assert(value: any, errorDescription?: string): void; declare namespace assert { export function isEqual(): void; } -declare function assert(value: any, errorDescription?: string): void; ``` ```ts title=main.ts @@ -249,7 +249,7 @@ assert(); ### Interfaces -```ts title=.d.ts +```ts title=image.d.ts interface Image { /** @tupleReturn */ getDimensions(): [number, number]; @@ -270,7 +270,7 @@ let o = image.getFlags(); ### Namespaces -```ts title=.d.ts +```ts title=love.d.ts declare namespace love { export let update: (delta: number) => void; /** @tupleReturn */ @@ -301,7 +301,7 @@ let p = love.graphics.newImage("file.png"); You'd only declare these if there were TypeScriptToLua compatible classes within the existing Lua code. It is definitely not recommended to define classes in ambient contexts for TypeScriptToLua, use interfaces instead. -```ts title=.d.ts +```ts title=x.d.ts declare class X { tuple(); } @@ -318,7 +318,7 @@ You may have to use the `@noResolution` annotation to tell TypeScriptToLua to no Module declarations need to be kept in _.d.ts_ files. -```ts title=.d.ts +```ts title=types.d.ts /** @noSelf */ declare module "image-size" { export function getimagewidth(filename: string): number; @@ -352,7 +352,7 @@ import * as customModule from "custom-module"; Unions can be used to tell TypeScript that a given type could be one of many other types. TypeScript can then pick up hints in the code to figure out what that type is at a given statement. -```ts title=main.ts +```ts declare interface PingResponse { type: "ping"; timeTaken: number; From c9876d9e8ea90477741bc30cc94a4cf81b478f42 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 10 Apr 2020 21:26:08 +0000 Subject: [PATCH 21/23] More formatting on Writing Declarations page more consistent --- docs/advanced/writing-declarations.md | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/advanced/writing-declarations.md b/docs/advanced/writing-declarations.md index 188b805d..76e0815a 100644 --- a/docs/advanced/writing-declarations.md +++ b/docs/advanced/writing-declarations.md @@ -8,7 +8,7 @@ If you need tips or help writing declarations, feel free to [join our Discord](h ## About Declaration Files -Declaration files end with the extension `.d.ts`. These contain pure ambient code. +Declaration files end with the extension _.d.ts_. These contain pure ambient code. For TypeScriptToLua, these files should contain information that describes the target Lua environment. @@ -17,12 +17,12 @@ This means functions, modules, variables and other members of the target Lua env They don't contain code that you would execute. Similar to how you'd write an interface in some other languages. TypeScriptToLua doesn't output any information from these files either. :::note -You can write ambient declarations inside `.ts` files as well. +You can write ambient declarations inside _.ts_ files as well. ::: ## Declare Keyword -The _declare_ keyword is used to say that the following declaration defines something that exists within global scope. Like something within the _\_G_ table in Lua. +The `declare` keyword is used to say that the following declaration defines something that exists within global scope. Like something within the `_G` table in Lua. This is useful for defining Lua's environment. @@ -51,7 +51,7 @@ print(_VERSION); // Editor and transpiler know what print and _VERSION are ``` :::note -You can use _declare_ to write ambient declarations inside `.ts` files. +You can use `declare` to write ambient declarations inside _.ts_ files. ::: ## Export Keyword @@ -60,7 +60,7 @@ The export keyword indicates something is exported and can be used by external c This also includes ambient interfaces, types, modules and other items that don't result in any transpiled code. -If a file named _lib.lua_ exists and returns a table with an _x_ field, you can write _lib.d.t.s_ as follows to tell TypeScript that _lib_ exists and what it provides. +If a file named _lib.lua_ exists and returns a table with an `x` field, you can write _lib.d.t.s_ as follows to tell TypeScript that _lib_ exists and what it provides. ```ts title=lib.d.ts export let x: number; @@ -105,9 +105,9 @@ The `export` keyword can be used in a `.ts` or `.d.ts` file. It tells the transp ## Self Parameter -TypeScript has a hidden _this_ parameter attached to every function. +TypeScript has a hidden `this` parameter attached to every function. -This causes TypeScriptToLua to treat every function as if _self_ exists as its first parameter. +This causes TypeScriptToLua to treat every function as if `self` exists as its first parameter. ```ts declare function assert(value: any): void; @@ -116,15 +116,15 @@ declare function assert(value: any): void; assert(true); // assert(_G, true) ``` -This allows users to modify _this_ inside a function and expect behaviour similar to what JavaScript does. +This allows users to modify `this` inside a function and expect behaviour similar to what JavaScript does. -But obviously Lua does not have a _self_ parameter for every function, so one of the three options must happen to tell TypeScriptToLua there is no "contextual parameter" (_self_): +But obviously Lua does not have a `self` parameter for every function, so one of the three options must happen to tell TypeScriptToLua there is no "contextual parameter" (`self`): -1. Use `this: void` as the first parameter of the function / method. This formally describes to TypeScript to not allow _this_ to be modified inside this function. (you could also use the _--noImplicitThis_ option to disallow _this_ to be modified if _this_ is of an _any_ type). +1. Use `this: void` as the first parameter of the function / method. This formally describes to TypeScript to not allow `this` to be modified inside this function. (you could also use the [noImplicitThis](../configuration.md#custom-options) option to disallow `this` to be modified if `this` is of an `any` type). 2. Use `@noSelf` in the comments of the declaration's owner (the namespace, module, object, etc). 3. Use `@noSelfInFile` at the beginning of the file in a comment to make sure every function defined in this file does not use a "contextual parameter". -Below is three ways to make _table.remove_ not use a "contextual parameter". +Below is three ways to make `table.remove` not use a "contextual parameter". ```ts declare namespace table { @@ -147,7 +147,7 @@ declare namespace table { } ``` -By doing this, the transpiler also figures out if it needs to use _:_ or _._ when invoking a function / method. +By doing this, the transpiler also figures out if it needs to use `:` or `.` when invoking a function / method. ## Comments and Annotations @@ -203,7 +203,7 @@ See [Compiler Annotations](compiler-annotations.md) page for more information. With TypeScript, by default, there are declarations that exist that describe something that doesn't exist in Lua (like `console.log`). -Using the _lib_ option can narrow down these declarations. +Using the `lib` option can narrow down these declarations. ```json title=tsconfig.json { @@ -213,7 +213,7 @@ Using the _lib_ option can narrow down these declarations. } ``` -It is possible to also use _noLib_ to remove every declaration but TypeScript NEEDS certain declarations to exist so they will have to be manually defined. TypeScriptToLua also treats certain declarations differently specifically if they came from the standard libs. So _noLib_ is not recommended. +It is possible to also use `noLib` to remove every declaration but TypeScript NEEDS certain declarations to exist so they will have to be manually defined. TypeScriptToLua also treats certain declarations differently specifically if they came from the standard libs. So `noLib` is not recommended. ## Advanced Types @@ -415,7 +415,7 @@ This can apply to numbers as well. ### Keyword Workarounds -Some functions in Lua can have names that are keywords in TypeScript (e.g., _try_, _catch_, _new_, etc). +Some functions in Lua can have names that are keywords in TypeScript (e.g., `try`, `catch`, `new`, etc). The parent to these kinds of functions will need to be represented as a JSON object. @@ -472,15 +472,15 @@ const d = v3.dot(v2); Using `import` can be important for making sure an _index.d.ts_ file contains all the declarations needed. -```ts +```ts title=index.d.ts import "./lib"; -// All declarations in lib will be included with this file +// All global declarations in lib will be included with this file export { Player } from "./Entities"; // The Player declaration is re-exported from this file ``` -It is also possible to place _import_ statements inside ambient modules and namespaces. +It is also possible to place `import` statements inside ambient modules and namespaces. ```ts declare module "mymodule" { @@ -491,7 +491,7 @@ declare module "mymodule" { ## NPM Publishing -It is possible to publish a list of declarations for other users to easily download via _npm_. +It is possible to publish a list of declarations for other users to easily download via [npm](https://www.npmjs.com/). ```bash npm init From b82828e3138cc4e7f22c04ff47f1c6cd6cda74e2 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 10 Apr 2020 22:25:07 +0000 Subject: [PATCH 22/23] Rewrite few sections on Writing Declarations page --- docs/advanced/writing-declarations.md | 100 +++++++++++++++++++++----- 1 file changed, 81 insertions(+), 19 deletions(-) diff --git a/docs/advanced/writing-declarations.md b/docs/advanced/writing-declarations.md index 76e0815a..2c359032 100644 --- a/docs/advanced/writing-declarations.md +++ b/docs/advanced/writing-declarations.md @@ -201,9 +201,7 @@ See [Compiler Annotations](compiler-annotations.md) page for more information. ## Environmental Declarations -With TypeScript, by default, there are declarations that exist that describe something that doesn't exist in Lua (like `console.log`). - -Using the `lib` option can narrow down these declarations. +By default, TypeScript includes global type declarations for both ECMAScript and web standards. TypeScriptToLua aims to support only standard ECMAScript feature set. To make TypeScript not suggest you to use unsupported browser builtins (including `window`, `document`, `console`, `setTimeout`) you can specify a `lib` option: ```json title=tsconfig.json { @@ -213,7 +211,7 @@ Using the `lib` option can narrow down these declarations. } ``` -It is possible to also use `noLib` to remove every declaration but TypeScript NEEDS certain declarations to exist so they will have to be manually defined. TypeScriptToLua also treats certain declarations differently specifically if they came from the standard libs. So `noLib` is not recommended. +It is also possible to use `noLib` to remove every standard declaration (to use TypeScriptToLua only for syntactic features with Lua standard library) but TypeScript **needs** certain declarations to exist so they will have to be manually defined, so using `noLib` is not recommended. ## Advanced Types @@ -299,17 +297,79 @@ let p = love.graphics.newImage("file.png"); ### Classes -You'd only declare these if there were TypeScriptToLua compatible classes within the existing Lua code. It is definitely not recommended to define classes in ambient contexts for TypeScriptToLua, use interfaces instead. +Because Lua doesn't have a strictly defined concept of a class, for TypeScriptToLua `class` declaration implies a very specific structure, built specifically for TypeScript compatibility. Because of that, usually you shouldn't use `declare class` for values coming from Lua. + +Most of Lua patterns used to simulate classes can be declared using interfaces instead. + +**Example 1**: a table with a static `new` method to construct new instances + +```lua +Box = {} +Box.__index = Box + +function Box.new(value) + local self = {} + setmetatable(self, Box) + self._value = value + return self +end + +function Box:get() + return self._value +end +``` + +```ts +interface Box { + get(): string; +} -```ts title=x.d.ts -declare class X { - tuple(); +interface BoxConstructor { + new: (this: void, value: string) => Box; } + +declare var Box: BoxConstructor; + +// Usage +const box = Box.new("foo"); +box.get(); ``` -```ts title=main.ts -let p = new X(); -p.tuple(); +**Example 2**: a callable table with extra static methods + +```lua +Box = {} + +local instance +function Box:getInstance() + if instance then return instance end + instance = Box("instance") + return instance +end + +setmetatable(Box, { + __call = function(_, value) + return { get = function() return value end } + end +}) +``` + +```ts +interface Box { + get(): string; +} + +interface BoxConstructor { + (this: void, value: string): Box; + getInstance(): Box; +} + +declare var Box: BoxConstructor; + +// Usage +const box = Box("foo"); +box.get(); +Box.getInstance().get(); ``` ### Ambient Modules @@ -399,19 +459,21 @@ getFile("player.png"); // Valid getFile("unknown.png"); // Invalid ``` -### String Enums +### Literal Types -TypeScript can check a string is valid. +String and number values can be used as types too. In combination with union types it can be used to represent a known set of values. ```ts -declare function draw(linetype: "solid" | "dashed", x1, y1, x2, y2): void; -draw("solid", 0, 0, 16, 16); // Valid -draw("rounded", 0, 0, 16, 16); // Invalid +declare function drawLine(type: "solid" | "dashed"): void; +drawLine("solid"); // Valid +drawLine("rounded"); // Invalid ``` -:::info -This can apply to numbers as well. -::: +```ts +declare function getSupportedColors(): 1 | 8 | 256 | 16777216; +getSupportedColors() === 8; // Valid +getSupportedColors() === 16; // Invalid +``` ### Keyword Workarounds From 3668198db91995a437b475a2ea0bb6652823ed63 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 10 Apr 2020 22:30:28 +0000 Subject: [PATCH 23/23] Reword local/global install note --- docs/getting-started.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 352e7a05..37ab0cae 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,6 +12,10 @@ TypeScriptToLua is built using [Node.js](https://nodejs.org/) and distributed vi npm install -D typescript-to-lua ``` +:::note +Installing `tstl` locally is recommended to keep your build reproducible and prevent version conflicts between projects. However, it is also possible to install it globally with `npm install --global typescript-to-lua` or run it without install using `npx typescript-to-lua`. +::: + ## Project setup TypeScriptToLua shares the configuration format with vanilla TypeScript. This file is called `tsconfig.json` and should be located in your project's root. @@ -38,10 +42,6 @@ Check out [Configuration](configuration.md) page for more information. Our command line interface is called `tstl` and it works almost exactly as TypeScript's `tsc`. -:::note -Even though it's possible to install `tstl` globally, it is considered a bad practice. For for testing purposes you can run it with `npx typescript-to-lua`. -::: - Since `tstl` is installed locally to your project, you cannot run it as a bare command in your terminal, so it's recommended to use it with [npm scripts](https://docs.npmjs.com/misc/scripts). ```json title=package.json @@ -65,6 +65,10 @@ npm run build npm run dev ``` +:::note +For testing purposes you also can run `tstl` directly from your terminal with `node_modules/.bin/tstl` or `npx --no-install tstl`. +::: + ## Declarations The real power of this transpiler is usage together with good declarations for the Lua API provided. Some examples of Lua interface declarations can be found here: