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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ coverage/

# IDEA IDEs
.idea/

typescript_lualib.lua
24 changes: 0 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,6 @@ More detailed documentation and info on writing declarations can be found [on th

`tstl -p path/to/tsconfig.json`

**Options**
```
tstl [options] [files...]

In addition to the options listed below you can also pass options for the
typescript compiler (For a list of options use tsc -h).

NOTES:
- The tsc options might have no effect.
- Options in tsconfig.json are prioritized.

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.

I think Options in tsconfig.json are prioritized. should be in here or the wiki.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed it so that CLI is prioritized. This is the same behaviour now as in tsc and probably any cli tool out there. So I don't think its needed to mention that, but it wouldn't hurt either if this is mentioned somewhere in the docs.

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.

That's much better. It only needed mentioning because it was different from tsc.


Options:
--help Show help [boolean]
--version Show version number [boolean]
--lt, --luaTarget Specify Lua target version.
[string] [choices: "JIT", "5.1", "5.2", "5.3"] [default: "JIT"]
--ah, --addHeader Specify if a header will be added to compiled files.
[boolean] [default: true]

Examples:
tstl path/to/file.ts [...] Compile files
tstl -p path/to/tsconfig.json Compile project
```

**Example tsconfig.json**
```
{
Expand Down
173 changes: 173 additions & 0 deletions src/CommandLineParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import * as ts from "typescript";
import * as yargs from 'yargs';
import * as fs from "fs";
import * as path from "path";

// ES6 syntax broken
const dedent = require("dedent");

export interface CompilerOptions extends ts.CompilerOptions {
addHeader?: boolean;
luaTarget?: string;
dontRequireLuaLib?: boolean;
}

export interface ParsedCommandLine extends ts.ParsedCommandLine {
options: CompilerOptions;
}

export class CLIError extends Error {

}

const optionDeclarations: { [key: string]: yargs.Options } = {
'luaTarget': {
alias: 'lt',
choices: ['JIT', '5.3'],
default: 'JIT',
describe: 'Specify Lua target version.',
type: 'string'
},
'addHeader': {
alias: 'ah',
describe: 'Specify if a header will be added to compiled files.',
default: false,
type: 'boolean'
},
'dontRequireLuaLib': {
describe: 'Dont require lua library that enables advanced Typescipt/JS functionality.',
default: false,
type: 'boolean'
},
};


/**
* Pares the supplied arguments.
* The result will include arguments supplied via CLI and arguments from tsconfig.
*/
export function parseCommandLine(args: string[]): ParsedCommandLine {
const parsedArgs = yargs
.usage(dedent(`Syntax: tstl [options] [files...]

In addition to the options listed below you can also pass options for the typescript compiler (For a list of options use tsc -h).
Some tsc options might have no effect.`))
.example('tstl path/to/file.ts [...]', 'Compile files')
.example('tstl -p path/to/tsconfig.json', 'Compile project')
.wrap(yargs.terminalWidth())
.options(optionDeclarations)
.fail((msg, err) => {
throw new CLIError(msg);
})
.parse(args)

let commandLine = ts.parseCommandLine(args);

// Run diagnostics to check for invalid tsc/tstl options
runDiagnostics(commandLine);

// Add TSTL options from CLI
addTSTLOptions(commandLine, parsedArgs);

// Load config
if (commandLine.options.project) {
findConfigFile(commandLine);
let configPath = commandLine.options.project;
let configContents = fs.readFileSync(configPath).toString();
const configJson = ts.parseConfigFileTextToJson(configPath, configContents);
commandLine = ts.parseJsonConfigFileContent(configJson.config, ts.sys, path.dirname(configPath), commandLine.options);
}

// Add TSTL options from tsconfig
addTSTLOptions(commandLine);

// Run diagnostics again to check for errors in tsconfig
runDiagnostics(commandLine);

if (commandLine.options.project && !commandLine.options.rootDir) {
commandLine.options.rootDir = path.dirname(commandLine.options.project);
}

if (!commandLine.options.rootDir) {
commandLine.options.rootDir = process.cwd();
}

if (!commandLine.options.outDir) {
commandLine.options.outDir = commandLine.options.rootDir;
}

return <ParsedCommandLine>commandLine;
}

function addTSTLOptions(commandLine: ts.ParsedCommandLine, additionalArgs?: yargs.Arguments, forceOverride?: boolean) {
additionalArgs = additionalArgs ? additionalArgs : commandLine.raw
// Add compiler options that are ignored by TS parsers
if (additionalArgs) {
for (const arg in additionalArgs) {
// dont override, this will prioritize CLI over tsconfig.
if (optionDeclarations[arg] && (!commandLine.options[arg] || forceOverride)) {
commandLine.options[arg] = additionalArgs[arg];
}
}
}
}

/** Check the current state of the ParsedCommandLine for errors */
function runDiagnostics(commandLine: ts.ParsedCommandLine) {
const tsInvalidCompilerOptionErrorCode = 5023;

if (commandLine.errors.length !== 0) {
// Generate a list of valid option names and aliases
let optionNames: string[] = [];
for (let key in optionDeclarations) {
optionNames.push(key);
let alias = optionDeclarations[key].alias;
if (alias) {
if (typeof alias === "string") {
optionNames.push(alias);
} else {
optionNames.push(...alias);
}
}
}

commandLine.errors.forEach((err) => {
let ignore = false;
// Ignore errors caused by tstl specific compiler options
if (err.code == tsInvalidCompilerOptionErrorCode) {
for (const optionName of optionNames) {
if (err.messageText.toString().indexOf(optionName) !== -1) {
ignore = true;
}
}
if (!ignore) {
throw new CLIError(`error TS${err.code}: ${err.messageText}`);
}
}
});
}
}

/** Find configFile, function from ts api seems to be broken? */
function findConfigFile(commandLine: ts.ParsedCommandLine) {
if (!commandLine.options.project) {
return;
}
let configPath = path.isAbsolute(commandLine.options.project) ? commandLine.options.project : path.join(process.cwd(), commandLine.options.project);
if (fs.statSync(configPath).isDirectory()) {
configPath = path.join(configPath, 'tsconfig.json');
} else if (fs.statSync(configPath).isFile() && path.extname(configPath) === ".ts") {
// Search for tsconfig upwards in directory hierarchy starting from the file path
let dir = path.dirname(configPath).split(path.sep);
for (let i = dir.length; i > 0; i--) {
const searchPath = dir.slice(0, i).join("/") + path.sep + "tsconfig.json";

// If tsconfig.json was found, stop searching
if (ts.sys.fileExists(searchPath)) {
configPath = searchPath;
break;
}
}
}
commandLine.options.project = configPath;
}
163 changes: 7 additions & 156 deletions src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@
import * as ts from "typescript";
import * as fs from "fs";
import * as path from "path";
import * as yargs from 'yargs'

// ES6 syntax broken
import dedent = require("dedent")

import { LuaTranspiler, TranspileError } from "./Transpiler";
import { TSHelper as tsEx } from "./TSHelper";

interface CompilerOptions extends ts.CompilerOptions {
addHeader?: boolean;
luaTarget?: string;
}
import { CompilerOptions, parseCommandLine } from "./CommandLineParser";

function compile(fileNames: string[], options: CompilerOptions): void {
let program = ts.createProgram(fileNames, options);
Expand All @@ -39,14 +31,6 @@ function compile(fileNames: string[], options: CompilerOptions): void {
process.exit(1);
}

if (!options.rootDir) {
options.rootDir = process.cwd();
}

if (!options.outDir) {
options.outDir = options.rootDir;
}

program.getSourceFiles().forEach(sourceFile => {
if (!sourceFile.isDeclarationFile) {
try {
Expand Down Expand Up @@ -83,146 +67,13 @@ function compile(fileNames: string[], options: CompilerOptions): void {
});

// Copy lualib to target dir
// This isnt run in sync because copyFileSync wont report errors.
fs.copyFile(path.resolve(__dirname, "../dist/lualib/typescript.lua"), path.join(options.outDir, "typescript_lualib.lua"), (err: NodeJS.ErrnoException) => {
if (err) {
console.log("ERROR: copying lualib to output.");
process.exit(1);
}
else {
process.exit(0);
}
});
}

function printAST(node: ts.Node, indent: number) {
let indentStr = "";
for (let i = 0; i < indent; i++) indentStr += " ";

console.log(indentStr + tsEx.enumName(node.kind, ts.SyntaxKind));
node.forEachChild(child => printAST(child, indent + 1));
}

// Polyfill for report diagnostics
function logError(commandLine: ts.ParsedCommandLine, tstlOptionKeys: ReadonlyArray<string>) {
const tsInvalidCompilerOptionErrorCode = 5023;
let ignoredErrorCount = 0;

if (commandLine.errors.length !== 0) {
commandLine.errors.forEach((err) => {
// Ignore errors caused by tstl specific compiler options
if (err.code == tsInvalidCompilerOptionErrorCode) {
for (const key of tstlOptionKeys) {
if (err.messageText.toString().indexOf(key) != -1) {
ignoredErrorCount += 1;
}
}
}
else {
console.log(err.messageText);
}
});
if (commandLine.errors.length > ignoredErrorCount) {
process.exit(1);
}
}
fs.copyFileSync(path.resolve(__dirname, "../dist/lualib/typescript.lua"), path.join(options.outDir, "typescript_lualib.lua"));
}

function executeCommandLine(args: ReadonlyArray<string>) {
const tstlOptions: {[key: string]: yargs.Options} = {
'lt': {
alias: 'luaTarget',
choices: ['JIT', '5.1', '5.2', '5.3'],
default: 'JIT',
describe: 'Specify Lua target version.',
type: 'string'
},
'ah': {
alias: 'addHeader',
describe: 'Specify if a header will be added to compiled files.',
default: true,
type: 'boolean'
}
};

const tstlOptionKeys = [];
for (let key in tstlOptions) {
let optionName = key;
if (tstlOptions[key].alias) {
optionName = tstlOptions[key].alias as string;
}

tstlOptionKeys.push(optionName);
}

const argv = yargs
.usage(dedent(`tstl [options] [files...]

In addition to the options listed below you can also pass options for the typescript compiler (For a list of options use tsc -h).

NOTES:
- The tsc options might have no effect.
- Options in tsconfig.json are prioritized.`))
.example('tstl path/to/file.ts [...]', 'Compile files')
.example('tstl -p path/to/tsconfig.json', 'Compile project')
.options(tstlOptions)
.argv;

let commandLine = ts.parseCommandLine(args);

logError(commandLine, tstlOptionKeys);

// Add tstl CLI options
for (const key of tstlOptionKeys) {
commandLine.options[key] = argv[key];
}

let configPath;
if (commandLine.options.project) {
configPath = path.isAbsolute(commandLine.options.project) ? commandLine.options.project : path.join(process.cwd(), commandLine.options.project);
if (fs.statSync(configPath).isDirectory()) {
configPath = path.join(configPath, 'tsconfig.json');
} else if (fs.statSync(configPath).isFile() && path.extname(configPath) === ".ts") {
// Search for tsconfig upwards in directory hierarchy starting from the file path
let dir = path.dirname(configPath).split(path.sep);
let found = false;
for (let i = dir.length; i > 0; i--) {
const searchPath = dir.slice(0, i).join("/") + path.sep + "tsconfig.json";

// If tsconfig.json was found, stop searching
if (ts.sys.fileExists(searchPath)) {
configPath = searchPath;
found = true;
break;
}
}

if (!found) {
console.error("Tried to build project but could not find tsconfig.json!");
process.exit(1);
}
}
commandLine.options.project = configPath;
let configContents = fs.readFileSync(configPath).toString();
const configJson = ts.parseConfigFileTextToJson(configPath, configContents);
commandLine = ts.parseJsonConfigFileContent(configJson.config, ts.sys, path.dirname(configPath), commandLine.options);

// Add compiler options that are ignored by TS parsers
// Options supplied in tsconfig are prioritized to allow for CLI defaults
for (const compilerOption in commandLine.raw.compilerOptions) {
if (tstlOptionKeys.indexOf(compilerOption) != -1) {
commandLine.options[compilerOption] = commandLine.raw.compilerOptions[compilerOption];
}
}
}

if (configPath && !commandLine.options.rootDir) {
commandLine.options.rootDir = path.dirname(configPath);
}

logError(commandLine, tstlOptionKeys);

compile(commandLine.fileNames, commandLine.options);
export function execCommandLine(argv?: string[]) {
argv = argv ? argv : process.argv.slice(2);
let commandLine = parseCommandLine(argv);
compile(commandLine.fileNames, commandLine.options)
}

executeCommandLine(process.argv.slice(2));
execCommandLine();
Loading