roblox-ts
Version:
<div align="center"><img width=25% src="https://i.imgur.com/yCjHmng.png"></div> <h1 align="center"><a href="https://roblox-ts.github.io/">roblox-ts</a></h1> <div align="center">A TypeScript-to-Lua Compiler for Roblox</div> <br> <div align="center"> <a hr
708 lines • 34.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const klaw_1 = __importDefault(require("klaw"));
const luamin_1 = require("luamin");
const path_1 = __importDefault(require("path"));
const ts = __importStar(require("ts-morph"));
const compiler_1 = require("./compiler");
const CompilerState_1 = require("./CompilerState");
const CompilerError_1 = require("./errors/CompilerError");
const DiagnosticError_1 = require("./errors/DiagnosticError");
const ProjectError_1 = require("./errors/ProjectError");
const RojoProject_1 = require("./RojoProject");
const utility_1 = require("./utility");
const MINIMUM_RBX_TYPES_VERSION = 203;
const LIB_PATH = path_1.default.resolve(__dirname, "..", "lib");
const ROJO_FILE_REGEX = /^.+\.project\.json$/;
const ROJO_DEFAULT_NAME = "default.project.json";
const ROJO_OLD_NAME = "roblox-project.json";
const IGNORED_DIAGNOSTIC_CODES = [
2688,
6054,
];
const LUA_EXT = ".lua";
function getLuaFiles(sourceFolder) {
return new Promise((resolve, reject) => {
const result = new Array();
klaw_1.default(sourceFolder)
.on("data", item => {
if (item.stats.isFile() && path_1.default.extname(item.path) === LUA_EXT) {
result.push(item.path);
}
})
.on("end", () => resolve(result))
.on("error", reject);
});
}
function joinIfNotAbsolute(basePath, relativePath) {
if (path_1.default.isAbsolute(relativePath)) {
return relativePath;
}
else {
return path_1.default.join(basePath, relativePath);
}
}
function copyLuaFiles(sourceFolder, destinationFolder, transform) {
return __awaiter(this, void 0, void 0, function* () {
(yield getLuaFiles(sourceFolder)).forEach((oldPath) => __awaiter(this, void 0, void 0, function* () {
const newPath = path_1.default.join(destinationFolder, path_1.default.relative(sourceFolder, oldPath));
let source = yield fs_extra_1.default.readFile(oldPath, "utf8");
if (transform) {
source = transform(source);
}
if (!(yield fs_extra_1.default.pathExists(newPath)) || (yield fs_extra_1.default.readFile(newPath, "utf8")) !== source) {
yield fs_extra_1.default.ensureFile(newPath);
yield fs_extra_1.default.writeFile(newPath, source);
}
}));
});
}
function cleanDeadLuaFiles(sourceFolder, destinationFolder) {
return __awaiter(this, void 0, void 0, function* () {
function searchForDeadFiles(dir) {
return __awaiter(this, void 0, void 0, function* () {
if (yield fs_extra_1.default.pathExists(dir)) {
for (const fileName of yield fs_extra_1.default.readdir(dir)) {
const filePath = path_1.default.join(dir, fileName);
try {
const stats = yield fs_extra_1.default.stat(filePath);
if (stats.isDirectory()) {
searchForDeadFiles(filePath);
if ((yield fs_extra_1.default.readdir(dir)).length === 0) {
fs_extra_1.default.remove(filePath);
console.log("delete", "dir", filePath);
}
}
else if (stats.isFile()) {
const relativeToDestFolder = path_1.default.relative(destinationFolder, filePath);
if (!(yield fs_extra_1.default.existsSync(path_1.default.join(sourceFolder, relativeToDestFolder)))) {
fs_extra_1.default.remove(filePath);
console.log("delete", "file", filePath);
}
}
}
catch (e) {
console.log("failed to clean", filePath);
}
}
}
});
}
yield searchForDeadFiles(destinationFolder);
});
}
function copyAndCleanDeadLuaFiles(sourceFolder, destinationFolder, transform) {
return __awaiter(this, void 0, void 0, function* () {
yield copyLuaFiles(sourceFolder, destinationFolder, transform);
yield cleanDeadLuaFiles(sourceFolder, destinationFolder);
});
}
var ProjectType;
(function (ProjectType) {
ProjectType[ProjectType["Game"] = 0] = "Game";
ProjectType[ProjectType["Bundle"] = 1] = "Bundle";
ProjectType[ProjectType["Package"] = 2] = "Package";
})(ProjectType = exports.ProjectType || (exports.ProjectType = {}));
class Project {
constructor(opts = {}) {
this.project = {};
this.compilerOptions = {};
this.projectPath = "";
this.projectInfo = { type: ProjectType.Package };
this.virtualFileNum = 1;
// cli mode
if (opts.project !== undefined && opts.includePath !== undefined && opts.rojo !== undefined) {
this.configFilePath = path_1.default.resolve(opts.project);
this.reloadProject();
this.noInclude = opts.noInclude === true;
this.includePath = joinIfNotAbsolute(this.projectPath, opts.includePath);
this.minify = opts.minify === true;
this.luaSourceTransformer = this.minify ? luamin_1.minify : undefined;
this.modulesPath = path_1.default.join(this.includePath, "node_modules");
this.rojoOverridePath = opts.rojo !== "" ? joinIfNotAbsolute(this.projectPath, opts.rojo) : undefined;
this.ci = opts.ci === true;
try {
this.validateRbxTypes();
}
catch (e) {
if (e instanceof ProjectError_1.ProjectError) {
console.log(utility_1.red("Compiler Error:"), e.message);
process.exit(1);
}
else {
throw e;
}
}
const rootDirPath = this.compilerOptions.rootDir;
if (!rootDirPath) {
throw new ProjectError_1.ProjectError("Expected 'rootDir' option in tsconfig.json!", ProjectError_1.ProjectErrorType.MissingRootDir);
}
this.rootDirPath = rootDirPath;
const outDirPath = this.compilerOptions.outDir;
if (!outDirPath) {
throw new ProjectError_1.ProjectError("Expected 'outDir' option in tsconfig.json!", ProjectError_1.ProjectErrorType.MissingOutDir);
}
this.outDirPath = outDirPath;
// filter out outDir .d.ts files
const outDir = this.project.getDirectory(outDirPath);
if (outDir) {
this.project.getSourceFiles().forEach(sourceFile => {
if (outDir.isAncestorOf(sourceFile)) {
this.project.removeSourceFile(sourceFile);
}
});
}
this.modulesDir = this.project.getDirectory(path_1.default.join(this.projectPath, "node_modules"));
this.rojoFilePath = this.getRojoFilePath();
this.reloadRojo();
}
else {
this.configFilePath = "";
this.includePath = "";
this.noInclude = true;
this.minify = false;
this.modulesPath = "";
this.rootDirPath = "";
this.outDirPath = "";
this.ci = false;
this.runtimeOverride = "local TS = ...; -- link to runtime library";
this.project = new ts.Project({
compilerOptions: {
allowSyntheticDefaultImports: true,
baseUrl: "src",
declaration: false,
downlevelIteration: true,
isolatedModules: true,
jsx: ts.ts.JsxEmit.React,
jsxFactory: "Roact.createElement",
module: ts.ts.ModuleKind.CommonJS,
noLib: true,
outDir: "out",
rootDir: ".",
strict: true,
target: ts.ts.ScriptTarget.ES2015,
typeRoots: ["node_modules/@rbxts"],
},
useVirtualFileSystem: true,
});
}
}
reloadProject() {
try {
fs_extra_1.default.accessSync(this.configFilePath, fs_extra_1.default.constants.R_OK | fs_extra_1.default.constants.W_OK);
}
catch (e) {
throw new Error("Project path does not exist!");
}
if (fs_extra_1.default.statSync(this.configFilePath).isDirectory()) {
this.configFilePath = path_1.default.resolve(this.configFilePath, "tsconfig.json");
}
if (!fs_extra_1.default.existsSync(this.configFilePath) || !fs_extra_1.default.statSync(this.configFilePath).isFile()) {
throw new Error("Cannot find tsconfig.json!");
}
this.projectPath = path_1.default.resolve(this.configFilePath, "..");
this.project = new ts.Project({
compilerOptions: {
configFilePath: this.configFilePath,
},
tsConfigFilePath: this.configFilePath,
});
this.compilerOptions = this.project.getCompilerOptions();
try {
this.validateCompilerOptions();
}
catch (e) {
if (e instanceof ProjectError_1.ProjectError) {
console.log(utility_1.red("Compiler Error:"), e.message);
process.exit(1);
}
else {
throw e;
}
}
}
reloadRojo() {
if (this.rojoFilePath) {
try {
this.rojoProject = RojoProject_1.RojoProject.fromPathSync(this.rojoFilePath);
}
catch (e) {
if (e instanceof RojoProject_1.RojoProjectError) {
console.log("Warning!", "Failed to load Rojo configuration. Import and export statements will not compile.", e.message);
}
}
}
if (this.rojoFilePath && this.rojoProject) {
const runtimeFsPath = path_1.default.join(this.includePath, "RuntimeLib.lua");
const runtimeLibPath = this.rojoProject.getRbxFromFile(runtimeFsPath).path;
if (!runtimeLibPath) {
throw new ProjectError_1.ProjectError(`A Rojo project file was found ( ${this.rojoFilePath} ), but contained no data for include folder!`, ProjectError_1.ProjectErrorType.BadRojoInclude);
}
else if (this.rojoProject.getNetworkType(runtimeFsPath) !== RojoProject_1.NetworkType.Unknown) {
throw new ProjectError_1.ProjectError(`Runtime library cannot be in a server-only or client-only container!`, ProjectError_1.ProjectErrorType.BadRojoInclude);
}
else if (this.rojoProject.isIsolated(runtimeFsPath)) {
throw new ProjectError_1.ProjectError(`Runtime library cannot be in an isolated container!`, ProjectError_1.ProjectErrorType.BadRojoInclude);
}
let type;
if (this.rojoProject.isGame()) {
type = ProjectType.Game;
}
else {
type = ProjectType.Bundle;
}
this.projectInfo = {
runtimeLibPath,
type,
};
}
else {
this.projectInfo = { type: ProjectType.Package };
}
}
validateCompilerOptions() {
const opts = this.compilerOptions;
const errors = new Array();
// required compiler options
if (opts.downlevelIteration !== true) {
errors.push(`${utility_1.yellow(`"downlevelIteration"`)} must be ${utility_1.yellow(`true`)}`);
}
if (opts.module !== ts.ts.ModuleKind.CommonJS) {
errors.push(`${utility_1.yellow(`"module"`)} must be ${utility_1.yellow(`"commonjs"`)}`);
}
if (opts.noLib !== true) {
errors.push(`${utility_1.yellow(`"noLib"`)} must be ${utility_1.yellow(`true`)}`);
}
if (opts.strict !== true) {
errors.push(`${utility_1.yellow(`"strict"`)} must be ${utility_1.yellow(`true`)}`);
}
if (opts.target !== ts.ts.ScriptTarget.ES2015) {
errors.push(`${utility_1.yellow(`"target"`)} must be ${utility_1.yellow(`"es6"`)}`);
}
if (opts.allowSyntheticDefaultImports !== true) {
errors.push(`${utility_1.yellow(`"allowSyntheticDefaultImports"`)} must be ${utility_1.yellow(`true`)}`);
}
if (opts.declaration !== true && opts.isolatedModules !== true) {
errors.push(`${utility_1.yellow(`"isolatedModules"`)} must be ${utility_1.yellow(`true`)}`);
}
const rbxTsModulesPath = path_1.default.join(this.projectPath, "node_modules", "@rbxts");
if (opts.typeRoots === undefined ||
opts.typeRoots.find(v => path_1.default.normalize(v) === rbxTsModulesPath) === undefined) {
errors.push(`${utility_1.yellow(`"typeRoots"`)} must be ${utility_1.yellow(`[ "node_modules/@rbxts" ]`)}`);
}
if (opts.types !== undefined) {
errors.push(`${utility_1.yellow(`"types"`)} must be ${utility_1.yellow(`undefined`)}`);
}
// configurable compiler options
if (opts.rootDir === undefined) {
errors.push(`${utility_1.yellow(`"rootDir"`)} must be defined`);
}
if (opts.outDir === undefined) {
errors.push(`${utility_1.yellow(`"outDir"`)} must be defined`);
}
// roact compiler options
if (opts.jsx !== undefined && opts.jsx !== ts.ts.JsxEmit.React) {
errors.push(`${utility_1.yellow(`"jsx"`)} must be ${utility_1.yellow(`"react"`)} or not defined`);
}
if (opts.jsxFactory !== undefined && opts.jsxFactory !== "Roact.createElement") {
errors.push(`${utility_1.yellow(`"jsxFactory"`)} must be ${utility_1.yellow(`"Roact.createElement"`)} or not defined`);
}
// throw if errors
if (errors.length > 0) {
throw new ProjectError_1.ProjectError(`Invalid "tsconfig.json" configuration!\n` +
"https://roblox-ts.github.io/docs/quick-start#project-folder-setup" +
"\n- " +
errors.join("\n- "), ProjectError_1.ProjectErrorType.BadTsConfig);
}
}
validateRbxTypes() {
const pkgJsonPath = path_1.default.join(this.projectPath, "node_modules", "@rbxts", "types", "package.json");
if (fs_extra_1.default.pathExistsSync(pkgJsonPath)) {
const pkgJson = JSON.parse(fs_extra_1.default.readFileSync(pkgJsonPath).toString());
if (pkgJson !== undefined) {
const versionStr = pkgJson.version;
if (versionStr !== undefined) {
const regexMatch = versionStr.match(/\d+$/g);
if (regexMatch !== null) {
const patchNumber = Number(regexMatch[0]);
if (!isNaN(patchNumber)) {
if (patchNumber >= MINIMUM_RBX_TYPES_VERSION) {
return;
}
else {
throw new ProjectError_1.ProjectError(`@rbxts/types is out of date!\n` +
utility_1.yellow(`Installed version: 1.0.${patchNumber}\n`) +
utility_1.yellow(`Minimum required version: 1.0.${MINIMUM_RBX_TYPES_VERSION}\n`) +
`Run 'npm i @rbxts/types' to fix this.`, ProjectError_1.ProjectErrorType.BadRbxTypes);
}
}
}
}
}
}
throw new ProjectError_1.ProjectError(`Could not find @rbxts/types!\n` + `Run 'npm i @rbxts/types' to fix this.`, ProjectError_1.ProjectErrorType.BadRbxTypes);
}
getRojoFilePath() {
if (this.rojoOverridePath) {
if (fs_extra_1.default.pathExistsSync(this.rojoOverridePath)) {
return this.rojoOverridePath;
}
}
else {
const candidates = new Array();
const defaultPath = path_1.default.join(this.projectPath, ROJO_DEFAULT_NAME);
if (fs_extra_1.default.pathExistsSync(defaultPath)) {
candidates.push(defaultPath);
}
for (const fileName of fs_extra_1.default.readdirSync(this.projectPath)) {
if (fileName !== ROJO_DEFAULT_NAME && (fileName === ROJO_OLD_NAME || ROJO_FILE_REGEX.test(fileName))) {
candidates.push(path_1.default.join(this.projectPath, fileName));
}
}
if (candidates.length > 1) {
console.log(utility_1.yellow(`Warning! Multiple *.project.json files found, using ${candidates[0]}`));
}
return candidates[0];
}
}
addFile(filePath) {
this.project.addExistingSourceFile(filePath);
}
removeFile(filePath) {
return __awaiter(this, void 0, void 0, function* () {
const sourceFile = this.project.getSourceFile(filePath);
if (sourceFile) {
this.project.removeSourceFile(sourceFile);
}
yield this.cleanDirRecursive(this.outDirPath);
});
}
refreshFile(filePath) {
return __awaiter(this, void 0, void 0, function* () {
const file = this.project.getSourceFile(filePath);
if (file) {
file.refreshFromFileSystem();
}
else {
this.project.addExistingSourceFile(filePath);
}
});
}
cleanDirRecursive(dir) {
return __awaiter(this, void 0, void 0, function* () {
if (yield fs_extra_1.default.pathExists(dir)) {
for (const name of yield fs_extra_1.default.readdir(dir)) {
const filePath = path_1.default.join(dir, name);
if ((yield fs_extra_1.default.stat(filePath)).isDirectory()) {
yield this.cleanDirRecursive(filePath);
if ((yield fs_extra_1.default.readdir(filePath)).length === 0) {
yield fs_extra_1.default.rmdir(filePath);
}
}
else {
let ext = path_1.default.extname(filePath);
let baseName = path_1.default.basename(filePath, ext);
let subext = path_1.default.extname(baseName);
baseName = path_1.default.basename(baseName, subext);
const relativeToOut = path_1.default.dirname(path_1.default.relative(this.outDirPath, filePath));
const rootPath = path_1.default.join(this.rootDirPath, relativeToOut);
if (ext === ".lua") {
let exists = false;
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".ts"));
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".tsx"));
exists =
exists ||
(baseName === "init" &&
(yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, "init") + subext + ".lua")));
exists =
exists ||
(baseName === "init" &&
(yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, "index") + subext + ".ts")));
exists =
exists ||
(baseName === "init" &&
(yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, "index") + subext + ".tsx")));
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".lua"));
if (!exists) {
yield fs_extra_1.default.remove(filePath);
console.log("remove", filePath);
}
}
else if (subext === ".d" && ext === ".ts") {
if (!this.compilerOptions.declaration) {
yield fs_extra_1.default.remove(filePath);
console.log("remove", filePath);
}
else {
ext = subext + ext;
baseName = path_1.default.basename(filePath, ext);
subext = path_1.default.extname(baseName);
baseName = path_1.default.basename(baseName, subext);
let exists = false;
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".d.ts"));
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".ts"));
exists = exists || (yield fs_extra_1.default.pathExists(path_1.default.join(rootPath, baseName) + subext + ".tsx"));
if (!exists) {
yield fs_extra_1.default.remove(filePath);
console.log("remove", filePath);
}
}
}
}
}
}
});
}
getRootDirOrThrow() {
if (!this.rootDirPath) {
throw new ProjectError_1.ProjectError("Could not find rootDir!", ProjectError_1.ProjectErrorType.MissingRootDir);
}
return this.rootDirPath;
}
copyModuleFiles() {
return __awaiter(this, void 0, void 0, function* () {
if (this.modulesDir && this.projectInfo.type !== ProjectType.Package) {
const modulesPath = this.modulesPath;
const rbxTsModulesPath = path_1.default.resolve(this.modulesDir.getPath(), "@rbxts");
if (yield fs_extra_1.default.pathExists(rbxTsModulesPath)) {
for (const name of yield fs_extra_1.default.readdir(rbxTsModulesPath)) {
const oldModulePath = path_1.default.join(rbxTsModulesPath, name);
const newModulePath = path_1.default.join(modulesPath, name);
yield copyAndCleanDeadLuaFiles(oldModulePath, newModulePath, this.luaSourceTransformer);
}
}
}
});
}
copyIncludeFiles() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.noInclude && this.projectInfo.type !== ProjectType.Package) {
yield copyLuaFiles(LIB_PATH, this.includePath, this.luaSourceTransformer);
}
});
}
copyLuaFiles() {
return __awaiter(this, void 0, void 0, function* () {
yield copyLuaFiles(this.rootDirPath, this.outDirPath, this.luaSourceTransformer);
});
}
copyDtsFiles() {
return __awaiter(this, void 0, void 0, function* () {
const dtsFiles = new Array();
yield new Promise(resolve => {
klaw_1.default(this.rootDirPath)
.on("data", item => {
if (item.path.endsWith(".d.ts")) {
dtsFiles.push(item.path);
}
})
.on("end", () => resolve());
});
return Promise.all(dtsFiles.map(filePath => {
return new Promise(resolve => {
const outPath = path_1.default.join(this.outDirPath, path_1.default.relative(this.rootDirPath, filePath));
fs_extra_1.default.readFile(filePath).then(buffer => {
const contents = buffer.toString();
fs_extra_1.default.ensureFile(outPath).then(() => {
fs_extra_1.default.writeFile(outPath, contents).then(() => resolve());
});
});
});
}));
});
}
compileAll() {
return __awaiter(this, void 0, void 0, function* () {
yield this.compileFiles(this.project.getSourceFiles());
if (process.exitCode === 0) {
yield this.copyLuaFiles();
if (this.compilerOptions.declaration) {
yield this.copyDtsFiles();
}
yield this.copyIncludeFiles();
yield this.copyModuleFiles();
}
});
}
compileFileByPath(filePath) {
return __awaiter(this, void 0, void 0, function* () {
const ext = path_1.default.extname(filePath);
if (ext === ".ts" || ext === ".tsx") {
const sourceFile = this.project.getSourceFile(filePath);
if (!sourceFile) {
throw new ProjectError_1.ProjectError(`No source file for Compiler.compileFileByPath() (filePath = ${filePath})`, ProjectError_1.ProjectErrorType.MissingSourceFile);
}
return this.compileFiles([sourceFile]);
}
else if (ext === ".lua") {
yield this.copyLuaFiles();
}
});
}
createCompilerState() {
return new CompilerState_1.CompilerState(this.rootDirPath, this.outDirPath, this.projectInfo, this.rojoProject, this.modulesDir, this.runtimeOverride);
}
compileSource(source) {
const sourceFile = this.project.createSourceFile(`file_${this.virtualFileNum++}.ts`, source);
let exception;
let compiledSource = "";
try {
compiledSource = compiler_1.compileSourceFile(this.createCompilerState(), sourceFile);
}
catch (e) {
exception = e;
}
const errors = this.getDiagnosticErrors([sourceFile]);
sourceFile.deleteImmediately();
if (errors.length > 0) {
throw new DiagnosticError_1.DiagnosticError(errors);
}
if (exception) {
throw exception;
}
return compiledSource;
}
getEmittedDtsFiles() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => {
const result = new Array();
klaw_1.default(this.outDirPath)
.on("data", item => {
if (item.stats.isFile() && item.path.endsWith(".d.ts")) {
result.push(item.path);
}
})
.on("end", () => resolve(result));
});
});
}
postProcessDtsFiles() {
return __awaiter(this, void 0, void 0, function* () {
return Promise.all((yield this.getEmittedDtsFiles()).map(filePath => new Promise(resolve => {
fs_extra_1.default.readFile(filePath).then(contentsBuffer => {
let fileContents = contentsBuffer.toString();
fileContents = fileContents.replace(/<reference types="([^."]+)" \/>/g, '<reference types="@rbxts/$1" />');
fs_extra_1.default.writeFile(filePath, fileContents).then(() => resolve());
});
})));
});
}
getDiagnosticErrors(files) {
const errors = new Array();
for (const file of files) {
const diagnostics = file
.getPreEmitDiagnostics()
.filter(diagnostic => diagnostic.getCategory() === ts.DiagnosticCategory.Error)
.filter(diagnostic => IGNORED_DIAGNOSTIC_CODES.indexOf(diagnostic.getCode()) === -1);
for (const diagnostic of diagnostics) {
const diagnosticFile = diagnostic.getSourceFile();
const line = diagnostic.getLineNumber();
let prefix = "";
if (diagnosticFile) {
prefix += path_1.default.relative(this.projectPath, diagnosticFile.getFilePath());
if (line) {
prefix += ":" + line;
}
prefix += " - ";
}
let messageText = diagnostic.getMessageText();
if (messageText instanceof ts.DiagnosticMessageChain) {
const textSegments = new Array();
let chain = messageText;
while (chain !== undefined) {
textSegments.push(chain.getMessageText());
chain = chain.getNext();
}
messageText = textSegments.join("\n");
}
errors.push(prefix + utility_1.red("Diagnostic Error: ") + messageText);
}
}
return errors;
}
compileFiles(files) {
return __awaiter(this, void 0, void 0, function* () {
yield this.cleanDirRecursive(this.outDirPath);
process.exitCode = 0;
const errors = this.getDiagnosticErrors(files);
try {
if (errors.length > 0) {
process.exitCode = 1;
throw new DiagnosticError_1.DiagnosticError(errors);
}
const sources = new Array();
for (const sourceFile of files) {
if (!sourceFile.isDeclarationFile()) {
const filePath = sourceFile.getFilePath();
const outPath = utility_1.transformPathToLua(this.rootDirPath, this.outDirPath, filePath);
let source = compiler_1.compileSourceFile(this.createCompilerState(), sourceFile);
if (this.luaSourceTransformer) {
source = this.luaSourceTransformer(source);
}
sources.push([outPath, source]);
}
}
for (const [filePath, contents] of sources) {
if (yield fs_extra_1.default.pathExists(filePath)) {
const oldContents = (yield fs_extra_1.default.readFile(filePath)).toString();
if (oldContents === contents) {
continue;
}
}
yield fs_extra_1.default.ensureFile(filePath);
yield fs_extra_1.default.writeFile(filePath, contents);
}
if (this.compilerOptions.declaration === true) {
yield this.project.emit({ emitOnlyDtsFiles: true });
yield this.postProcessDtsFiles();
}
}
catch (e) {
// do not silence errors for CI tests
if (this.ci) {
throw e;
}
if (e instanceof CompilerError_1.CompilerError) {
const node = e.node;
if (ts.TypeGuards.isSourceFile(node)) {
console.log("%s - %s %s", path_1.default.relative(this.projectPath, e.node.getSourceFile().getFilePath()), utility_1.red("Compiler Error:"), e.message);
}
else {
console.log("%s:%d:%d - %s %s", path_1.default.relative(this.projectPath, e.node.getSourceFile().getFilePath()), e.node.getStartLineNumber(), e.node.getNonWhitespaceStart() - e.node.getStartLinePos(), utility_1.red("Compiler Error:"), e.message);
}
}
else if (e instanceof ProjectError_1.ProjectError) {
console.log(utility_1.red("Project Error:"), e.message);
}
else if (e instanceof DiagnosticError_1.DiagnosticError) {
console.log(e.toString());
}
else {
throw e;
}
process.exitCode = 1;
}
});
}
}
exports.Project = Project;
//# sourceMappingURL=Project.js.map