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
21 changes: 14 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions src/CompilerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface TransformerImport {
export type CompilerOptions = OmitIndexSignature<ts.CompilerOptions> & {
noImplicitSelf?: boolean;
noHeader?: boolean;
luaBundle?: string;
luaBundleEntry?: string;
luaTarget?: LuaTarget;
luaLibImport?: LuaLibImportKind;
noHoisting?: boolean;
Expand All @@ -41,3 +43,42 @@ export enum LuaTarget {
Lua53 = "5.3",
LuaJIT = "JIT",
}

export function validateOptions(options: CompilerOptions): ts.Diagnostic[] {
const diagnostics: ts.Diagnostic[] = [];

if (options.luaBundle && !options.luaBundleEntry) {
diagnostics.push(configErrorDiagnostic(`'luaBundleEntry' is required when 'luaBundle' is enabled.`));
}

if (options.luaBundle && options.luaLibImport === LuaLibImportKind.Inline) {
diagnostics.push(
configWarningDiagnostic(
`Using 'luaBundle' with 'luaLibImport: "inline"' might generate duplicate code. ` +
`It is recommended to use 'luaLibImport: "require"'`
)
);
}

return diagnostics;
}

const configErrorDiagnostic = (message: string): ts.Diagnostic => ({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO diagnostic factories should be specified, that would look better once we'll have diagnostic codes

file: undefined,
start: undefined,
length: undefined,
category: ts.DiagnosticCategory.Error,
code: 0,
source: "typescript-to-lua",
messageText: message,
});

const configWarningDiagnostic = (message: string): ts.Diagnostic => ({
file: undefined,
start: undefined,
length: undefined,
category: ts.DiagnosticCategory.Warning,
code: 0,
source: "typescript-to-lua",
messageText: message,
});
67 changes: 32 additions & 35 deletions src/Emit.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as path from "path";
import * as ts from "typescript";
import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions";
import { LuaLibImportKind } from "./CompilerOptions";
import { EmitHost, TranspiledFile } from "./Transpile";
import { normalizeSlashes } from "./utils";

const trimExt = (filePath: string) => filePath.slice(0, -path.extname(filePath).length);
import { normalizeSlashes, trimExtension } from "./utils";

export interface OutputFile {
name: string;
Expand All @@ -13,18 +11,15 @@ export interface OutputFile {

let lualibContent: string;
export function emitTranspiledFiles(
options: CompilerOptions,
program: ts.Program,
transpiledFiles: TranspiledFile[],
emitHost: EmitHost = ts.sys
): OutputFile[] {
let { rootDir, outDir, outFile, luaLibImport } = options;

const configFileName = options.configFilePath as string | undefined;
// TODO: Use getCommonSourceDirectory
const baseDir = configFileName ? path.dirname(configFileName) : process.cwd();
const options = program.getCompilerOptions();
let { outDir, luaLibImport, luaBundle } = options;

rootDir = rootDir || baseDir;
outDir = outDir ? path.resolve(baseDir, outDir) : rootDir;
const rootDir = program.getCommonSourceDirectory();
outDir = outDir || rootDir;

const files: OutputFile[] = [];
for (const { fileName, lua, sourceMap, declaration, declarationMap } of transpiledFiles) {
Expand All @@ -33,14 +28,8 @@ export function emitTranspiledFiles(
outPath = path.resolve(outDir, path.relative(rootDir, fileName));
}

// change extension or rename to outFile
if (outFile) {
outPath = path.isAbsolute(outFile) ? outFile : path.resolve(baseDir, outFile);
} else {
outPath = trimExt(outPath) + ".lua";
}

outPath = normalizeSlashes(outPath);
// change extension
outPath = normalizeSlashes(trimExtension(outPath) + ".lua");

if (lua !== undefined) {
files.push({ name: outPath, text: lua });
Expand All @@ -51,30 +40,38 @@ export function emitTranspiledFiles(
}

if (declaration !== undefined) {
files.push({ name: trimExt(outPath) + ".d.ts", text: declaration });
files.push({ name: trimExtension(outPath) + ".d.ts", text: declaration });
}

if (declarationMap !== undefined) {
files.push({ name: trimExt(outPath) + ".d.ts.map", text: declarationMap });
files.push({ name: trimExtension(outPath) + ".d.ts.map", text: declarationMap });
}
}

if (luaLibImport === LuaLibImportKind.Require || luaLibImport === LuaLibImportKind.Always) {
if (lualibContent === undefined) {
const lualibBundle = emitHost.readFile(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"));
if (lualibBundle !== undefined) {
lualibContent = lualibBundle;
} else {
throw new Error("Could not load lualib bundle from ./dist/lualib/lualib_bundle.lua");
if (
!luaBundle &&
(luaLibImport === undefined ||
luaLibImport === LuaLibImportKind.Require ||
luaLibImport === LuaLibImportKind.Always)
) {
const lualibRequired = files.some(f => f.text && f.text.includes(`require("lualib_bundle")`));
if (lualibRequired) {
if (lualibContent === undefined) {
const lualibBundle = emitHost.readFile(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"));
if (lualibBundle !== undefined) {
lualibContent = lualibBundle;
} else {
throw new Error("Could not load lualib bundle from ./dist/lualib/lualib_bundle.lua");
}
}
}

let outPath = path.resolve(rootDir, "lualib_bundle.lua");
if (outDir !== rootDir) {
outPath = path.join(outDir, path.relative(rootDir, outPath));
}
let outPath = path.resolve(rootDir, "lualib_bundle.lua");
if (outDir !== rootDir) {
outPath = path.join(outDir, path.relative(rootDir, outPath));
}

files.push({ name: normalizeSlashes(outPath), text: lualibContent });
files.push({ name: normalizeSlashes(outPath), text: lualibContent });
}
}

return files;
Expand Down
10 changes: 7 additions & 3 deletions src/LuaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export class LuaPrinter {

public constructor(private options: CompilerOptions, private emitHost: EmitHost) {}

public print(block: tstl.Block, luaLibFeatures?: Set<LuaLibFeature>, sourceFile = ""): [string, string] {
public print(
block: tstl.Block,
luaLibFeatures?: Set<LuaLibFeature>,
sourceFile = ""
): [string, string, SourceNode] {
// Add traceback lualib if sourcemap traceback option is enabled
if (this.options.sourceMapTraceback) {
if (luaLibFeatures === undefined) {
Expand Down Expand Up @@ -71,7 +75,7 @@ export class LuaPrinter {
codeResult = codeResult.replace("{#SourceMapTraceback}", stackTraceOverride);
}

return [codeResult, sourceMap.toString()];
return [codeResult, sourceMap.toString(), rootSourceNode];
}

private printInlineSourceMap(sourceMap: SourceMapGenerator): string {
Expand Down Expand Up @@ -113,7 +117,7 @@ export class LuaPrinter {
}

if (luaLibFeatures) {
const luaLibImport = this.options.luaLibImport || LuaLibImportKind.Inline;
const luaLibImport = this.options.luaLibImport || LuaLibImportKind.Require;
// Require lualib bundle
if (
(luaLibImport === LuaLibImportKind.Require && luaLibFeatures.size > 0) ||
Expand Down
10 changes: 1 addition & 9 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Decorator, DecoratorKind } from "./Decorator";
import * as tstl from "./LuaAST";
import * as TSTLErrors from "./TSTLErrors";
import { EmitResolver } from "./LuaTransformer";
import { formatPathToLuaPath } from "./utils";

export enum ContextType {
None,
Expand Down Expand Up @@ -1060,15 +1061,6 @@ export function getExportPath(fileName: string, options: ts.CompilerOptions): st
return formatPathToLuaPath(absolutePath.replace(absoluteRootDirPath, "").slice(1));
}

export function formatPathToLuaPath(filePath: string): string {
filePath = filePath.replace(/\.json$/, "");
if (process.platform === "win32") {
// Windows can use backslashes
filePath = filePath.replace(/\.\\/g, "").replace(/\\/g, ".");
}
return filePath.replace(/\.\//g, "").replace(/\//g, ".");
}

export function isBuiltinErrorTypeName(name: string): boolean {
return builtinErrorTypeNames.has(name);
}
31 changes: 26 additions & 5 deletions src/Transpile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SourceNode } from "source-map";
import * as ts from "typescript";
import { CompilerOptions } from "./CompilerOptions";
import { bundleTranspiledFiles } from "./bundle";
import { CompilerOptions, validateOptions } from "./CompilerOptions";
import * as diagnosticFactories from "./diagnostics";
import { Block } from "./LuaAST";
import { LuaPrinter } from "./LuaPrinter";
Expand All @@ -14,6 +16,8 @@ export interface TranspiledFile {
sourceMap?: string;
declaration?: string;
declarationMap?: string;
/** @internal */
sourceMapNode?: SourceNode;
}

export interface TranspileResult {
Expand All @@ -31,6 +35,7 @@ export interface TranspileOptions {
}

export interface EmitHost {
getCurrentDirectory(): string;
readFile(path: string): string | undefined;
}

Expand All @@ -44,7 +49,7 @@ export function transpile({
}: TranspileOptions): TranspileResult {
const options = program.getCompilerOptions() as CompilerOptions;

const diagnostics: ts.Diagnostic[] = [];
const diagnostics = validateOptions(options);
let transpiledFiles: TranspiledFile[] = [];

const updateTranspiledFile = (fileName: string, update: Omit<TranspiledFile, "fileName">) => {
Expand All @@ -57,7 +62,11 @@ export function transpile({
};

if (options.noEmitOnError) {
const preEmitDiagnostics = [...program.getOptionsDiagnostics(), ...program.getGlobalDiagnostics()];
const preEmitDiagnostics = [
...diagnostics,
...program.getOptionsDiagnostics(),
...program.getGlobalDiagnostics(),
];

if (targetSourceFiles) {
for (const sourceFile of targetSourceFiles) {
Expand All @@ -82,8 +91,8 @@ export function transpile({
try {
const [luaAst, lualibFeatureSet] = transformer.transform(sourceFile);
if (!options.noEmit && !options.emitDeclarationOnly) {
const [lua, sourceMap] = printer.print(luaAst, lualibFeatureSet, sourceFile.fileName);
updateTranspiledFile(sourceFile.fileName, { luaAst, lua, sourceMap });
const [lua, sourceMap, sourceNode] = printer.print(luaAst, lualibFeatureSet, sourceFile.fileName);
updateTranspiledFile(sourceFile.fileName, { luaAst, lua, sourceMap, sourceMapNode: sourceNode });
}
} catch (err) {
if (!(err instanceof TranspileError)) throw err;
Expand Down Expand Up @@ -144,5 +153,17 @@ export function transpile({
transpiledFiles = [];
}

if (options.luaBundle && options.luaBundleEntry) {
const [bundleDiagnostics, bundle] = bundleTranspiledFiles(
options.luaBundle,
options.luaBundleEntry,
transpiledFiles,
program,
emitHost
);
diagnostics.push(...bundleDiagnostics);
transpiledFiles = [bundle];
}

return { diagnostics, transpiledFiles };
}
Loading