Skip to main content

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.

Example:

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

Internally, to process Abstract Syntax Tree of a TypeScript program, TypeScriptToLua implements the 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:

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:

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

printer is a function that overrides the standard implementation of the Lua AST printer. It receives some information about the file and the transformed Lua AST. See the LuaPrinter page for more information.

Example:

import { SourceNode } from "source-map";
import * as ts from "typescript";
import * as tstl from "typescript-to-lua";

const CUSTOM_COMMENT_HEADER = "-- This code was generated with a custom plugin!\n";

class CustomPrinter extends tstl.LuaPrinter {
/* Override printFile */
protected printFile(file: tstl.File): SourceNode {
const originalResult = super.printFile(file);
// Add header comment at the top of the file
return this.createSourceNode(file, [`${CUSTOM_COMMENT_HEADER} ${this.luaFile}\n`, originalResult]);
}

/* Override printBoolean */
public printBooleanLiteral(expression: tstl.BooleanLiteral): SourceNode {
// Print any boolean as 'true'
return this.createSourceNode(expression, "true");
}
}

const plugin: tstl.Plugin = {
printer: (program: ts.Program, emitHost: tstl.EmitHost, fileName: string, file: tstl.File) =>
new CustomPrinter(emitHost, program, fileName).print(file),
};

export default plugin;

beforeTransform

The beforeTransform function on plugins is called after gathering the TypeScript program and compiler options, but before any transformation to Lua is done.

It can be used to set up the plugin

import * as ts from "typescript";
import * as tstl from "typescript-to-lua";

class Plugin implements tstl.Plugin {
public beforeTransform(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost) {
console.log("Starting transformation of program", program, "with options", options);
}
}

const plugin = new Plugin();
export default plugin;

afterPrint

The afterPrint function is called after tstl has translated the input program to Lua, but before resolving dependencies and before bundling if configured. You can use this to modify the list of output files and do direct string modifications to them.

import * as ts from "typescript";
import * as tstl from "typescript-to-lua";

const plugin: tstl.Plugin = {
afterPrint(
program: ts.Program,
options: tstl.CompilerOptions,
emitHost: tstl.EmitHost,
result: tstl.ProcessedFile[],
) {
// Add a comment to the start of all created Lua files
for (const file of result) {
file.code = "-- Comment added by afterPrint plugin\n" + file.code;
}
},
};

export default plugin;

beforeEmit

The beforeEmit function is called after the input program has been translated to Lua, after external dependencies have been resolved and included, and after bundling (if configured).

import * as ts from "typescript";
import * as tstl from "typescript-to-lua";

const plugin: tstl.Plugin = {
beforeEmit(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost, result: tstl.EmitFile[]) {
// Add a comment to the start of all output files
for (const file of result) {
file.code = "-- Comment added by beforeEmit plugin\n" + file.code;
}
},
};

export default plugin;

afterEmit

The afterEmit function is called after all output files have been written to disk. This might be useful if you need to somehow post-process the output Lua, for example by feeding it to a Lua minifier.

import * as ts from "typescript";
import * as tstl from "typescript-to-lua";

const plugin: tstl.Plugin = {
afterEmit(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost, result: tstl.EmitFile[]) {
for (const emittedFile of result) {
console.log(`Emitted file ${emittedFile.outputPath}`);
}
},
};

export default plugin;

moduleResolution

You can use the moduleResolution function to modify how the tstl module resolution works. It provides you with the require path, the file requiring that module, the tsconfig options used to compile the project, and the tstl EmitHost.

You can use this to intercept the regular module resolution and provide your own. If this function returns undefined, tstl will fall back on its own module resolution and try to resolve the file as usual.

import * as tstl from "typescript-to-lua";

const plugin: tstl.Plugin = {
moduleResolution(
moduleIdentifier: string,
requiringFile: string,
options: tstl.CompilerOptions,
emitHost: tstl.EmitHost,
) {
// If "foo" is required, resolve it as 'bar.lua'
if (moduleIdentifier === "foo") {
return "bar";
}
},
};

export default plugin;