Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/LuaAST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
// because we don't create the AST from text

import * as ts from "typescript";
import { LuaLibFeature } from "./transformation/utils/lualib";
import { castArray } from "./utils";

export enum SyntaxKind {
File,
Block,

// Statements
Expand Down Expand Up @@ -186,6 +188,30 @@ export function getOriginalPos(node: Node): TextRange {
return { line: node.line, column: node.column };
}

export interface File extends Node {
kind: SyntaxKind.File;
statements: Statement[];
luaLibFeatures: Set<LuaLibFeature>;
trivia: string;
}

export function isFile(node: Node): node is File {
return node.kind === SyntaxKind.File;
}

export function createFile(
statements: Statement[],
luaLibFeatures: Set<LuaLibFeature>,
trivia: string,
tsOriginal?: ts.Node
): File {
const file = createNode(SyntaxKind.File, tsOriginal) as File;
file.statements = statements;
file.luaLibFeatures = luaLibFeatures;
file.trivia = trivia;
return file;
}

export interface Block extends Node {
kind: SyntaxKind.Block;
statements: Statement[];
Expand Down
32 changes: 12 additions & 20 deletions src/LuaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as lua from "./LuaAST";
import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib";
import { isValidLuaIdentifier } from "./transformation/utils/safe-names";
import { EmitHost } from "./transpilation";
import { intersperse, trimExtension, normalizeSlashes } from "./utils";
import { intersperse, normalizeSlashes, trimExtension } from "./utils";

// https://www.lua.org/pil/2.4.html
// https://www.ecma-international.org/ecma-262/10.0/index.html#table-34
Expand Down Expand Up @@ -71,13 +71,7 @@ function isSimpleExpression(expression: lua.Expression): boolean {

type SourceChunk = string | SourceNode;

export type Printer = (
program: ts.Program,
emitHost: EmitHost,
fileName: string,
block: lua.Block,
luaLibFeatures: Set<LuaLibFeature>
) => PrintResult;
export type Printer = (program: ts.Program, emitHost: EmitHost, fileName: string, file: lua.File) => PrintResult;

export interface PrintResult {
code: string;
Expand All @@ -87,7 +81,7 @@ export interface PrintResult {

export function createPrinter(printers: Printer[]): Printer {
if (printers.length === 0) {
return (program, emitHost, fileName, ...args) => new LuaPrinter(emitHost, program, fileName).print(...args);
return (program, emitHost, fileName, file) => new LuaPrinter(emitHost, program, fileName).print(file);
} else if (printers.length === 1) {
return printers[0];
} else {
Expand Down Expand Up @@ -148,17 +142,17 @@ export class LuaPrinter {
}
}

public print(block: lua.Block, luaLibFeatures: Set<LuaLibFeature>): PrintResult {
public print(file: lua.File): PrintResult {
// Add traceback lualib if sourcemap traceback option is enabled
if (this.options.sourceMapTraceback) {
luaLibFeatures.add(LuaLibFeature.SourceMapTraceBack);
file.luaLibFeatures.add(LuaLibFeature.SourceMapTraceBack);
}

const sourceRoot = this.options.sourceRoot
? // According to spec, sourceRoot is simply prepended to the source name, so the slash should be included
this.options.sourceRoot.replace(/[\\/]+$/, "") + "/"
: "";
const rootSourceNode = this.printImplementation(block, luaLibFeatures);
const rootSourceNode = this.printFile(file);
const sourceMap = this.buildSourceMap(sourceRoot, rootSourceNode);

let code = rootSourceNode.toString();
Expand Down Expand Up @@ -203,8 +197,8 @@ export class LuaPrinter {
return `__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${mapString});`;
}

private printImplementation(block: lua.Block, luaLibFeatures: Set<LuaLibFeature>): SourceNode {
let header = "";
private printFile(file: lua.File): SourceNode {
let header = file.trivia;

if (!this.options.noHeader) {
header += "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]\n";
Expand All @@ -213,23 +207,21 @@ export class LuaPrinter {
const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require;
if (
luaLibImport === LuaLibImportKind.Always ||
(luaLibImport === LuaLibImportKind.Require && luaLibFeatures.size > 0)
(luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0)
) {
// Require lualib bundle
header += 'require("lualib_bundle");\n';
} else if (luaLibImport === LuaLibImportKind.Inline && luaLibFeatures.size > 0) {
} else if (luaLibImport === LuaLibImportKind.Inline && file.luaLibFeatures.size > 0) {
// Inline lualib features
header += "-- Lua Library inline imports\n";
header += loadLuaLibFeatures(luaLibFeatures, this.emitHost);
header += loadLuaLibFeatures(file.luaLibFeatures, this.emitHost);
}

if (this.options.sourceMapTraceback) {
header += "{#SourceMapTraceback}\n";
}

const fileBlockNode = this.printBlock(block);

return this.concatNodes(header, fileBlockNode);
return this.concatNodes(header, ...this.printStatementArray(file.statements));
}

protected pushIndent(): void {
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/context/visitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export type VisitorResult<T extends ts.Node> = T extends ExpressionLikeNode
: T extends StatementLikeNode
? OneToManyVisitorResult<lua.Statement>
: T extends ts.SourceFile
? lua.Block
? lua.File
: OneToManyVisitorResult<lua.Node>;

export type Visitor<T extends ts.Node> = FunctionVisitor<T> | ObjectVisitor<T>;
Expand Down
22 changes: 3 additions & 19 deletions src/transformation/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as ts from "typescript";
import * as lua from "../LuaAST";
import { LuaLibFeature } from "../LuaLib";
import { getOrUpdate } from "../utils";
import { ObjectVisitor, TransformationContext, VisitorMap, Visitors } from "./context";
import { getUsedLuaLibFeatures } from "./utils/lualib";
import { standardVisitors } from "./visitors";

export function createVisitorMap(customVisitors: Visitors[]): VisitorMap {
Expand All @@ -29,23 +27,9 @@ export function createVisitorMap(customVisitors: Visitors[]): VisitorMap {
return visitorMap;
}

export interface TransformSourceFileResult {
luaAst: lua.Block;
luaLibFeatures: Set<LuaLibFeature>;
diagnostics: ts.Diagnostic[];
}

export function transformSourceFile(
program: ts.Program,
sourceFile: ts.SourceFile,
visitorMap: VisitorMap
): TransformSourceFileResult {
export function transformSourceFile(program: ts.Program, sourceFile: ts.SourceFile, visitorMap: VisitorMap) {
const context = new TransformationContext(program, sourceFile, visitorMap);
const [luaAst] = context.transformNode(sourceFile) as [lua.Block];
const [file] = context.transformNode(sourceFile) as [lua.File];

return {
luaAst,
luaLibFeatures: getUsedLuaLibFeatures(context),
diagnostics: context.diagnostics,
};
return { file, diagnostics: context.diagnostics };
}
4 changes: 3 additions & 1 deletion src/transformation/visitors/sourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as lua from "../../LuaAST";
import { assert } from "../../utils";
import { FunctionVisitor } from "../context";
import { createExportsIdentifier } from "../utils/lua-ast";
import { getUsedLuaLibFeatures } from "../utils/lualib";
import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope";
import { hasExportEquals } from "../utils/typescript";

Expand Down Expand Up @@ -39,5 +40,6 @@ export const transformSourceFileNode: FunctionVisitor<ts.SourceFile> = (node, co
}
}

return lua.createBlock(statements, node);
const trivia = node.getFullText().match(/^#!.*\r?\n/)?.[0] ?? "";
return lua.createFile(statements, getUsedLuaLibFeatures(context), trivia, node);
};
10 changes: 3 additions & 7 deletions src/transpilation/transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,14 @@ export function getProgramTranspileResult(
const visitorMap = createVisitorMap(plugins.map(p => p.visitors).filter(isNonNull));
const printer = createPrinter(plugins.map(p => p.printer).filter(isNonNull));
const processSourceFile = (sourceFile: ts.SourceFile) => {
const { luaAst, luaLibFeatures, diagnostics: transformDiagnostics } = transformSourceFile(
program,
sourceFile,
visitorMap
);
const { file, diagnostics: transformDiagnostics } = transformSourceFile(program, sourceFile, visitorMap);

diagnostics.push(...transformDiagnostics);
if (!options.noEmit && !options.emitDeclarationOnly) {
const printResult = printer(program, emitHost, sourceFile.fileName, luaAst, luaLibFeatures);
const printResult = printer(program, emitHost, sourceFile.fileName, file);
const sourceRootDir = program.getCommonSourceDirectory();
const fileName = path.resolve(sourceRootDir, sourceFile.fileName);
transpiledFiles.push({ sourceFiles: [sourceFile], fileName, luaAst, ...printResult });
transpiledFiles.push({ sourceFiles: [sourceFile], fileName, luaAst: file, ...printResult });
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/transpilation/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface BaseFile {

export interface ProcessedFile extends BaseFile {
fileName: string;
luaAst?: lua.Block;
luaAst?: lua.File;
/** @internal */
sourceMapNode?: SourceNode;
}
Expand Down
11 changes: 11 additions & 0 deletions test/unit/__snapshots__/file.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`shebang CRLF 1`] = `
"#!/usr/bin/env lua
foo = true"
`;

exports[`shebang LF 1`] = `
"#!/usr/bin/env lua
foo = true"
`;
27 changes: 27 additions & 0 deletions test/unit/file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as util from "../util";

describe("JSON", () => {
test.each([0, "", [], [1, "2", []], { a: "b" }, { a: { b: "c" } }])("JSON (%p)", json => {
util.testModule(JSON.stringify(json)).setMainFileName("main.json").expectToEqual(json);
});

test("empty file throws runtime error", () => {
util.testModule("")
.setMainFileName("main.json")
.expectToEqual(new util.ExecutionError("Unexpected end of JSON input"));
});
});

describe("shebang", () => {
test("LF", () => {
util.testModule`#!/usr/bin/env lua\n
const foo = true;
`.expectLuaToMatchSnapshot();
});

test("CRLF", () => {
util.testModule`#!/usr/bin/env lua\r\n
const foo = true;
`.expectLuaToMatchSnapshot();
});
});
11 changes: 0 additions & 11 deletions test/unit/json.spec.ts

This file was deleted.