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
96 changes: 52 additions & 44 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@
},
"dependencies": {
"typescript": "^2.9.2",
"yargs": "^11.1.0"
"yargs": "^12.0.1"
},
"devDependencies": {
"@types/glob": "^5.0.35",
"@types/node": "^9.6.23",
"@types/yargs": "^11.0.0",
"@types/yargs": "^11.1.1",
"alsatian": "^2.2.1",
"circular-json": "^0.5.5",
"codecov": "3.0.2",
Expand Down
47 changes: 39 additions & 8 deletions src/CommandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export class CLIError extends Error {

}

const optionDeclarations: { [key: string]: yargs.Options } = {
export interface YargsOptions {
[key: string]: yargs.Options;
}

export const optionDeclarations: YargsOptions = {
luaLibImport: {
choices: ["inline", "require", "none"],
default: "inline",
Expand All @@ -38,11 +42,33 @@ const optionDeclarations: { [key: string]: yargs.Options } = {
},
};

/**
* Removes defaults from the arguments.
* Returns a tuple where [0] is a copy of the options without defaults and [1] is the extracted defaults.
*/
function getYargOptionsWithoutDefaults(options: YargsOptions): [YargsOptions, yargs.Arguments] {
// options is a deep object, Object.assign or {...options} still keeps the referece
const copy = JSON.parse(JSON.stringify(options));

const optionDefaults: yargs.Arguments = {_: null, $0: null};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What are these 2 values for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The typing of the object requires those as its yargs.Arguments (non-optional props). I didn't want to use as any later when they are returned as yargs.Arguments, although in this context it's probably fine.

yargs.Arguments type: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/yargs/index.d.ts#L227

I suppose one option is to use Partial<> and then force it to yargs.Arguments to retain at least some type info.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks a bit strange but okay, I'm fine with it for now.

for (const optionName in copy) {
const section = copy[optionName];

optionDefaults[optionName] = section.default;
delete section.default;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't really like deleting, shouldn't the result be an empty object anyway?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if I understand your question. What result should be an empty object anyway?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nevermind I missread, looks a bit strange but okay.

}

return [copy, optionDefaults];
}

/**
* Pares the supplied arguments.
* The result will include arguments supplied via CLI and arguments from tsconfig.
*/
export function parseCommandLine(args: string[]): ParsedCommandLine {
// Get a copy of the options without defaults to prevent defaults overriding project config
const [tstlOptions, tstlDefaults] = getYargOptionsWithoutDefaults(optionDeclarations);

const parsedArgs = yargs
.usage("Syntax: tstl [options] [files...]\n\n" +
"In addition to the options listed below you can also pass options" +
Expand All @@ -51,7 +77,7 @@ export function parseCommandLine(args: string[]): ParsedCommandLine {
.example("tstl path/to/file.ts [...]", "Compile files")
.example("tstl -p path/to/tsconfig.json", "Compile project")
.wrap(yargs.terminalWidth())
.options(optionDeclarations)
.options(tstlOptions)
.fail((msg, err) => {
throw new CLIError(msg);
})
Expand Down Expand Up @@ -82,6 +108,9 @@ export function parseCommandLine(args: string[]): ParsedCommandLine {
// Add TSTL options from tsconfig
addTSTLOptions(commandLine);

// Add TSTL defaults last
addTSTLOptions(commandLine, tstlDefaults);

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

Expand Down Expand Up @@ -158,13 +187,15 @@ export function findConfigFile(commandLine: ts.ParsedCommandLine): void {
if (!commandLine.options.project) {
throw new CLIError(`error no base path provided, could not find config.`);
}
let configPath;
/* istanbul ignore else: Testing else part is not really possible via automated tests */
if (path.isAbsolute(commandLine.options.project)) {
configPath = commandLine.options.project;
} else {
let configPath = commandLine.options.project;
// If the project path is wrapped in double quotes, remove them
if (/^".*"$/.test(configPath)) {
configPath = configPath.substring(1, configPath.length - 1);
}
/* istanbul ignore if: Testing else part is not really possible via automated tests */
if (!path.isAbsolute(configPath)) {
// TODO check if commandLine.options.project can even contain non absolute paths
configPath = path.join(process.cwd(), commandLine.options.project);
configPath = path.join(process.cwd(), configPath);
}
if (fs.statSync(configPath).isDirectory()) {
configPath = path.join(configPath, "tsconfig.json");
Expand Down
40 changes: 40 additions & 0 deletions test/unit/compiler/configuration/mixed/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Expect, Test, TestCase, Teardown } from "alsatian";
import * as path from 'path';
import * as fs from 'fs';
import * as ts from "typescript";

import { CompilerOptions, findConfigFile, parseCommandLine, ParsedCommandLine, optionDeclarations } from "../../../../../src/CommandLineParser";
import { LuaLibImportKind } from "../../../../../src/Transpiler";

export class MixedConfigurationTests {

@Test("tsconfig.json mixed with cmd line args")
public tsconfigMixedWithCmdLineArgs() {
const rootPath = __dirname;
const tsConfigPath = path.join(rootPath, "project-tsconfig.json");
const expectedTsConfig = ts.parseJsonConfigFileContent(
ts.parseConfigFileTextToJson(tsConfigPath, fs.readFileSync(tsConfigPath).toString()).config,
ts.sys,
path.dirname(tsConfigPath)
);

const parsedArgs = parseCommandLine([
"-p",
`"${tsConfigPath}"`,
"--luaLibImport",
LuaLibImportKind.Inline,
`${path.join(rootPath, 'test.ts')}`,
]);

Expect(parsedArgs.options).toEqual(<CompilerOptions>{
...expectedTsConfig.options,
// Overridden by cmd args (set to "none" in project-tsconfig.json)
luaLibImport: LuaLibImportKind.Inline,
// Only set in tsconfig, TSTL default is "JIT"
luaTarget: "5.1",
// Only present in TSTL dfaults
noHeader: optionDeclarations["noHeader"].default,
project: tsConfigPath
});
}
}
8 changes: 8 additions & 0 deletions test/unit/compiler/configuration/mixed/project-tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"outDir": "./dist/foo/bar",
"rootDir": "./src/foo/bar"
},
"luaTarget": "5.1",
"luaLibImport": "none"
}