Skip to content

Commit 7c353f1

Browse files
committed
generate lualib with plugins
1 parent a434ab2 commit 7c353f1

File tree

5 files changed

+140
-9
lines changed

5 files changed

+140
-9
lines changed

src/lualib-build/plugin.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { SourceNode } from "source-map";
2+
import * as ts from "typescript";
3+
import * as tstl from "..";
4+
5+
function lualibFileVisitor(file: ts.SourceFile, context: tstl.TransformationContext): tstl.File {
6+
// Get all imports in file
7+
const imports = file.statements.filter(ts.isImportDeclaration);
8+
9+
const importNames = new Set<string>();
10+
for (const { importClause } of imports) {
11+
if (importClause?.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
12+
for (const { name } of importClause.namedBindings.elements) {
13+
importNames.add(name.text);
14+
}
15+
}
16+
}
17+
18+
// Transpile file as normal with tstl
19+
const fileResult = context.superTransformNode(file)[0] as tstl.File;
20+
21+
// Find all exports assignments
22+
const exportInitializers = new Map<string, tstl.Expression>();
23+
for (const s of fileResult.statements) {
24+
if (tstl.isAssignmentStatement(s) && isExportTableIndex(s.left[0])) {
25+
exportInitializers.set(s.left[0].index.value, s.right[0]);
26+
}
27+
}
28+
29+
// Replace export aliases with initializers
30+
for (let i = 0; i < fileResult.statements.length; i++) {
31+
const statement = fileResult.statements[i];
32+
if (isExportAlias(statement)) {
33+
const name = statement.left[0];
34+
fileResult.statements[i] = tstl.createAssignmentStatement(name, exportInitializers.get(name.text));
35+
}
36+
}
37+
38+
// Filter out import/export statements
39+
const shouldIgnoreImportsExports = (node: tstl.Node) =>
40+
!isExportTableDeclaration(node) &&
41+
!isRequire(node) &&
42+
!isImport(node, importNames) &&
43+
!isExportAssignment(node) &&
44+
!isExportsReturn(node);
45+
const filteredStatements = fileResult.statements.filter(shouldIgnoreImportsExports);
46+
47+
if (filteredStatements.length > 0) {
48+
// If there are local statements, wrap them in a do ... end with exports outside
49+
const exports = tstl.createVariableDeclarationStatement([...exportInitializers.keys()].map(k => tstl.createIdentifier(k)));
50+
51+
fileResult.statements = [exports, tstl.createDoStatement(filteredStatements)];
52+
} else {
53+
const newStatements = [];
54+
for (const [exportName, initializer] of exportInitializers) {
55+
newStatements.push(tstl.createVariableDeclarationStatement(tstl.createIdentifier(exportName), initializer));
56+
}
57+
fileResult.statements = newStatements;
58+
}
59+
60+
return fileResult;
61+
}
62+
63+
class LuaLibPrinter extends tstl.LuaPrinter {
64+
// Strip all exports during print
65+
public printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode {
66+
if (tstl.isIdentifier(expression.table) && expression.table.text === "____exports" && tstl.isStringLiteral(expression.index)) {
67+
return super.printExpression(tstl.createIdentifier(expression.index.value));
68+
}
69+
return super.printTableIndexExpression(expression);
70+
}
71+
}
72+
73+
const plugin: tstl.Plugin = {
74+
visitors: {
75+
[ts.SyntaxKind.SourceFile]: lualibFileVisitor,
76+
},
77+
printer: (program, emitHost, fileName, file) => new LuaLibPrinter(emitHost, program, fileName).print(file),
78+
};
79+
80+
// eslint-disable-next-line import/no-default-export
81+
export default plugin;
82+
83+
function isExportTableDeclaration(node: tstl.Node): node is tstl.VariableDeclarationStatement & { left: [] } {
84+
return tstl.isVariableDeclarationStatement(node) && isExportTable(node.left[0]);
85+
}
86+
87+
function isExportTable(node: tstl.Node): node is tstl.Identifier {
88+
return tstl.isIdentifier(node) && node.text === "____exports";
89+
}
90+
91+
function isExportTableIndex(node: tstl.Node): node is tstl.TableIndexExpression & { index: tstl.StringLiteral } {
92+
return tstl.isTableIndexExpression(node) && isExportTable(node.table) && tstl.isStringLiteral(node.index);
93+
}
94+
95+
function isExportAlias(node: tstl.Node): node is tstl.VariableDeclarationStatement {
96+
return tstl.isVariableDeclarationStatement(node) && node.right !== undefined && isExportTableIndex(node.right[0]);
97+
}
98+
99+
function isExportAssignment(node: tstl.Node) {
100+
return tstl.isAssignmentStatement(node) && isExportTableIndex(node.left[0]);
101+
}
102+
103+
function isRequire(node: tstl.Node) {
104+
return (
105+
tstl.isVariableDeclarationStatement(node) &&
106+
node.right &&
107+
tstl.isCallExpression(node.right[0]) &&
108+
tstl.isIdentifier(node.right[0].expression) &&
109+
node.right[0].expression.text === "require"
110+
);
111+
}
112+
113+
function isImport(node: tstl.Node, importNames: Set<string>) {
114+
return tstl.isVariableDeclarationStatement(node) && importNames.has(node.left[0].text);
115+
}
116+
117+
function isExportsReturn(node: tstl.Node) {
118+
return (
119+
tstl.isReturnStatement(node) &&
120+
tstl.isIdentifier(node.expressions[0]) &&
121+
node.expressions[0].text === "____exports"
122+
);
123+
}

src/lualib/Promise.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
/* eslint-disable @typescript-eslint/promise-function-async */
22

3+
import { __TS__PromiseState } from "./PromiseState";
4+
35
// Promises implemented based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
46
// and https://promisesaplus.com/
57

6-
enum __TS__PromiseState {
7-
Pending,
8-
Fulfilled,
9-
Rejected,
10-
}
11-
128
type FulfillCallback<TData, TResult> = (value: TData) => TResult | PromiseLike<TResult>;
139
type RejectCallback<TResult> = (reason: any) => TResult | PromiseLike<TResult>;
1410

@@ -27,7 +23,7 @@ function __TS__IsPromiseLike<T>(thing: unknown): thing is PromiseLike<T> {
2723
return thing instanceof __TS__Promise;
2824
}
2925

30-
class __TS__Promise<T> implements Promise<T> {
26+
export class __TS__Promise<T> implements Promise<T> {
3127
public state = __TS__PromiseState.Pending;
3228
public value?: T;
3329
public rejectionReason?: any;

src/lualib/PromiseAll.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
2+
3+
import { __TS__Promise } from "./Promise";
4+
import { __TS__PromiseState } from "./PromiseState";
5+
26
// eslint-disable-next-line @typescript-eslint/promise-function-async
3-
function __TS__PromiseAll<T>(this: void, iterable: Iterable<T | PromiseLike<T>>): Promise<T[]> {
7+
export function __TS__PromiseAll<T>(this: void, iterable: Iterable<T | PromiseLike<T>>): Promise<T[]> {
48
const results: T[] = [];
59

610
const toResolve = new LuaTable<number, PromiseLike<T>>();

src/lualib/PromiseState.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export enum __TS__PromiseState {
2+
Pending,
3+
Fulfilled,
4+
Rejected,
5+
}

src/lualib/tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
},
1212
"tstl": {
1313
"luaLibImport": "none",
14-
"noHeader": true
14+
"noHeader": true,
15+
"luaPlugins": [
16+
{ "name": "../lualib-build/plugin.ts" }
17+
]
1518
}
1619
}

0 commit comments

Comments
 (0)