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
113 changes: 88 additions & 25 deletions src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,91 @@ import { LuaLibImportKind, LuaTarget, LuaTranspiler, TranspileError } from "./Tr

export function compile(argv: string[]): void {
const commandLine = parseCommandLine(argv);
compileFilesWithOptions(commandLine.fileNames, commandLine.options);
/* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */
if (commandLine.options.watch) {
watchWithOptions(commandLine.fileNames, commandLine.options);
} else {
compileFilesWithOptions(commandLine.fileNames, commandLine.options);
}
}

export function compileFilesWithOptions(fileNames: string[], options: CompilerOptions): void {
if (!options.luaTarget) {
options.luaTarget = LuaTarget.LuaJIT;
/* istanbul ignore next: tested in test/compiler/watchmode.spec with subproccess */
export function watchWithOptions(fileNames: string[], options: CompilerOptions): void {
let host: ts.WatchCompilerHost<ts.SemanticDiagnosticsBuilderProgram>;
let config = false;
if (options.project) {
config = true;
host = ts.createWatchCompilerHost(
options.project,
options,
ts.sys,
ts.createSemanticDiagnosticsBuilderProgram
);
} else {
host = ts.createWatchCompilerHost(
fileNames,
options,
ts.sys,
ts.createSemanticDiagnosticsBuilderProgram
);
}

host.afterProgramCreate = program => {
const status = emitFilesAndReportErrors(program.getProgram());
const errorDiagnostic: ts.Diagnostic = {
category: undefined,
code: 6194,
file: undefined,
length: 0,
messageText: "Found 0 errors. Watching for file changes.",
start: 0,
};
if (status !== 0) {
errorDiagnostic.messageText = "Found Errors. Watching for file changes.";
errorDiagnostic.code = 6193;
}
host.onWatchStatusChange(
errorDiagnostic,
host.getNewLine(),
program.getCompilerOptions()
);
};

if (config) {
ts.createWatchProgram(
host as ts.WatchCompilerHostOfConfigFile<ts.SemanticDiagnosticsBuilderProgram>);
} else {
ts.createWatchProgram(
host as ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.SemanticDiagnosticsBuilderProgram>);
}
}

export function compileFilesWithOptions(fileNames: string[], options: CompilerOptions): void {
const program = ts.createProgram(fileNames, options);

emitFilesAndReportErrors(program);
}

function emitFilesAndReportErrors(program: ts.Program): number {
const options = program.getCompilerOptions() as CompilerOptions;

const checker = program.getTypeChecker();

// Get all diagnostics, ignore unsupported extension
const diagnostics = ts.getPreEmitDiagnostics(program).filter(diag => diag.code !== 6054);
diagnostics.forEach(diagnostic => {
if (diagnostic.file) {
const { line, character } =
diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
console.log(
`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`
);
} else {
console.log(
`${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`
);
}
});
diagnostics.forEach(reportDiagnostic);

// If there are errors dont emit
if (diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error).length > 0) {
console.log("Stopping compilation process because of errors.");
process.exit(1);
if (!options.watch) {
process.exit(1);
} else {
return 1;
}
}

program.getSourceFiles().forEach(sourceFile => {

if (!sourceFile.isDeclarationFile) {
try {
const rootDir = options.rootDir;
Expand Down Expand Up @@ -101,16 +150,15 @@ export function compileFilesWithOptions(fileNames: string[], options: CompilerOp
path.join(options.outDir, "lualib_bundle.lua")
);
}

return 0;
}

export function createTranspiler(checker: ts.TypeChecker,
options: ts.CompilerOptions,
sourceFile: ts.SourceFile): LuaTranspiler {
let luaTargetTranspiler: LuaTranspiler;
switch (options.luaTarget) {
case LuaTarget.LuaJIT:
luaTargetTranspiler = new LuaTranspilerJIT(checker, options, sourceFile);
break;
case LuaTarget.Lua51:
luaTargetTranspiler = new LuaTranspiler51(checker, options, sourceFile);
break;
Expand All @@ -121,9 +169,24 @@ export function createTranspiler(checker: ts.TypeChecker,
luaTargetTranspiler = new LuaTranspiler53(checker, options, sourceFile);
break;
default:
// should not happen
throw Error("No luaTarget Specified please ensure a target is set!");
luaTargetTranspiler = new LuaTranspilerJIT(checker, options, sourceFile);
break;
}

return luaTargetTranspiler;
}

function reportDiagnostic(diagnostic: ts.Diagnostic): void {
if (diagnostic.file) {
const { line, character } =
diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
console.log(
`${diagnostic.code}: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`
);
} else {
console.log(
`${diagnostic.code}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`
);
}
}
4 changes: 4 additions & 0 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export abstract class LuaTranspiler {
this.classStack = [];
this.exportStack = [];
this.luaLibFeatureSet = new Set<LuaLibFeature>();

if (!this.options.luaTarget) {
this.options.luaTarget = LuaTarget.LuaJIT;
}
}

public pushIndent(): void {
Expand Down
3 changes: 3 additions & 0 deletions test/compiler/projects/watchmode/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"luaTarget": "JIT"
}
1 change: 1 addition & 0 deletions test/compiler/projects/watchmode/watch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class MyTest {}
1 change: 1 addition & 0 deletions test/compiler/testfiles/watch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class MyTest {}
5 changes: 5 additions & 0 deletions test/compiler/watcher_proccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { compile } from "../../src/Compiler";

process.on("message", args => {
compile(args);
});
68 changes: 68 additions & 0 deletions test/compiler/watchmode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { AsyncTest, Expect, Setup, TestCase, Timeout } from "alsatian";
import { fork } from "child_process";
import * as fs from "fs";
import * as path from "path";

export class CompilerWatchModeTest {

@TestCase(["-w", path.join(__dirname, "./testfiles/watch.ts")],
path.join(__dirname, "./testfiles/watch.ts"))
@TestCase(["-w", "-p", path.join(__dirname, "./projects/watchmode/")],
path.join(__dirname, "./projects/watchmode/watch.ts"))
@AsyncTest("Watch single File")
@Timeout(16000)
public async testSingle(args: string[], fileToChange: string): Promise<void> {
fileToChange = fileToChange;
const fileToChangeOut = fileToChange.replace(".ts", ".lua");

const child = fork(path.join(__dirname, "watcher_proccess.ts"));
child.send(args);

await this.waitForFileExists(fileToChangeOut, 9000)
.catch(err => console.error(err));

Expect(fs.existsSync(fileToChangeOut)).toBe(true);

const initialResultLua = fs.readFileSync(fileToChangeOut);
const originalTS = fs.readFileSync(fileToChange);

fs.unlinkSync(fileToChangeOut);

fs.writeFileSync(fileToChange, "class MyTest2 {}");

await this.waitForFileExists(fileToChangeOut, 5000)
.catch(err => console.error(err));

const updatedResultLua = fs.readFileSync(fileToChangeOut).toString();

Expect(initialResultLua).not.toEqual(updatedResultLua);

fs.writeFileSync(fileToChange, originalTS);

fs.unlinkSync(fileToChangeOut);

child.kill();
}

private waitForFileExists(filepath: string, timeout: number = 3000): Promise<void> {
const interval = 200;
return new Promise((resolve, reject) => {
const intervalTimerId = setInterval(
() => {
if (fs.existsSync(filepath)) {
clearTimeout(timeoutId);
clearInterval(intervalTimerId);
resolve();
}
},
interval);

const timeoutId = setTimeout(
() => {
clearInterval(intervalTimerId);
reject(new Error("Wating for file timed out!"));
},
timeout);
});
}
}
16 changes: 0 additions & 16 deletions test/unit/compiler.spec.ts

This file was deleted.