Skip to content

Commit 3860842

Browse files
ark120202Perryvw
authored andcommitted
Refactor CLI file structure and fix --project path resolution on Windows (#743)
* Fix `--project` path resolution on Windows * Add tests * Remove unnecessary condition part * Reset working directory after running tests * Refactor CLI directory structure * Move `tstl` out of `cli` directory * Rename `config` to `tsconfig` * Remove `cli/tsconfig` module reexport
1 parent 0f5c5aa commit 3860842

16 files changed

+399
-241
lines changed

package-lock.json

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@
4444
"typescript": "^3.6.2"
4545
},
4646
"devDependencies": {
47+
"@types/fs-extra": "^8.0.1",
4748
"@types/glob": "^7.1.1",
4849
"@types/jest": "^24.0.15",
4950
"@types/node": "^11.13.14",
5051
"@types/resolve": "0.0.8",
5152
"fengari": "^0.1.4",
53+
"fs-extra": "^8.1.0",
5254
"javascript-stringify": "^2.0.0",
5355
"jest": "^24.8.0",
5456
"jest-circus": "^24.8.0",

src/Emit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as path from "path";
22
import * as ts from "typescript";
33
import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions";
4-
import { TranspiledFile, EmitHost } from "./Transpile";
4+
import { EmitHost, TranspiledFile } from "./Transpile";
5+
import { normalizeSlashes } from "./utils";
56

67
const trimExt = (filePath: string) => filePath.slice(0, -path.extname(filePath).length);
7-
const normalizeSlashes = (filePath: string) => filePath.replace(/\\/g, "/");
88

99
export interface OutputFile {
1010
name: string;

src/TSTransformers.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as path from "path";
22
import * as resolve from "resolve";
33
import * as ts from "typescript";
4+
import * as cliDiagnostics from "./cli/diagnostics";
45
import { CompilerOptions, TransformerImport } from "./CompilerOptions";
56
import * as diagnosticFactories from "./diagnostics";
67
import { noImplicitSelfTransformer } from "./NoImplicitSelfTransformer";
@@ -111,7 +112,7 @@ function resolveTransformerFactory(
111112
): { error?: ts.Diagnostic; factory?: TransformerFactory } {
112113
if (typeof transform !== "string") {
113114
const optionName = `${transformerOptionPath}.transform`;
114-
return { error: diagnosticFactories.compilerOptionRequiresAValueOfType(optionName, "string") };
115+
return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "string") };
115116
}
116117

117118
let resolved: string;
@@ -167,18 +168,18 @@ function loadTransformer(
167168
break;
168169
default: {
169170
const optionName = `--${transformerOptionPath}.type`;
170-
return { error: diagnosticFactories.argumentForOptionMustBe(optionName, "program") };
171+
return { error: cliDiagnostics.argumentForOptionMustBe(optionName, "program") };
171172
}
172173
}
173174

174175
if (typeof after !== "boolean") {
175176
const optionName = `${transformerOptionPath}.after`;
176-
return { error: diagnosticFactories.compilerOptionRequiresAValueOfType(optionName, "boolean") };
177+
return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "boolean") };
177178
}
178179

179180
if (typeof afterDeclarations !== "boolean") {
180181
const optionName = `${transformerOptionPath}.afterDeclarations`;
181-
return { error: diagnosticFactories.compilerOptionRequiresAValueOfType(optionName, "boolean") };
182+
return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "boolean") };
182183
}
183184

184185
if (typeof transformer === "function") {

src/cli/diagnostics.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as ts from "typescript";
2+
3+
export const tstlOptionsAreMovingToTheTstlObject = (tstl: Record<string, any>): ts.Diagnostic => ({
4+
file: undefined,
5+
start: undefined,
6+
length: undefined,
7+
category: ts.DiagnosticCategory.Warning,
8+
code: 0,
9+
source: "typescript-to-lua",
10+
messageText:
11+
'TSTL options are moving to the "tstl" object. Adjust your tsconfig to look like\n' +
12+
`"tstl": ${JSON.stringify(tstl, undefined, 4)}`,
13+
});
14+
15+
export const watchErrorSummary = (errorCount: number): ts.Diagnostic => ({
16+
file: undefined,
17+
start: undefined,
18+
length: undefined,
19+
category: ts.DiagnosticCategory.Message,
20+
code: errorCount === 1 ? 6193 : 6194,
21+
messageText:
22+
errorCount === 1
23+
? "Found 1 error. Watching for file changes."
24+
: `Found ${errorCount} errors. Watching for file changes.`,
25+
});
26+
27+
const createCommandLineError = <Args extends any[]>(code: number, getMessage: (...args: Args) => string) => (
28+
...args: Args
29+
): ts.Diagnostic => ({
30+
file: undefined,
31+
start: undefined,
32+
length: undefined,
33+
category: ts.DiagnosticCategory.Error,
34+
code,
35+
messageText: getMessage(...args),
36+
});
37+
38+
export const unknownCompilerOption = createCommandLineError(
39+
5023,
40+
(name: string) => `Unknown compiler option '${name}'.`
41+
);
42+
43+
export const compilerOptionRequiresAValueOfType = createCommandLineError(
44+
5024,
45+
(name: string, type: string) => `Compiler option '${name}' requires a value of type ${type}.`
46+
);
47+
48+
export const optionProjectCannotBeMixedWithSourceFilesOnACommandLine = createCommandLineError(
49+
5042,
50+
() => "Option 'project' cannot be mixed with source files on a command line."
51+
);
52+
53+
export const cannotFindATsconfigJsonAtTheSpecifiedDirectory = createCommandLineError(
54+
5057,
55+
(dir: string) => `Cannot find a tsconfig.json file at the specified directory: '${dir}'.`
56+
);
57+
58+
export const theSpecifiedPathDoesNotExist = createCommandLineError(
59+
5058,
60+
(dir: string) => `The specified path does not exist: '${dir}'.`
61+
);
62+
63+
export const compilerOptionExpectsAnArgument = createCommandLineError(
64+
6044,
65+
(name: string) => `Compiler option '${name}' expects an argument.`
66+
);
67+
68+
export const argumentForOptionMustBe = createCommandLineError(
69+
6046,
70+
(name: string, values: string) => `Argument for '${name}' option must be: ${values}.`
71+
);
72+
73+
export const optionCanOnlyBeSpecifiedInTsconfigJsonFile = createCommandLineError(
74+
6064,
75+
(name: string) => `Option '${name}' can only be specified in 'tsconfig.json' file.`
76+
);
77+
78+
export const optionBuildMustBeFirstCommandLineArgument = createCommandLineError(
79+
6369,
80+
() => "Option '--build' must be the first command line argument."
81+
);

src/cli/information.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { optionDeclarations } from "./parse";
2+
3+
export const { version } = require("../../package.json");
4+
export const versionString = `Version ${version}`;
5+
6+
const helpString = `
7+
Syntax: tstl [options] [files...]
8+
9+
Examples: tstl path/to/file.ts [...]
10+
tstl -p path/to/tsconfig.json
11+
12+
In addition to the options listed below you can also pass options
13+
for the typescript compiler (For a list of options use tsc -h).
14+
Some tsc options might have no effect.
15+
`.trim();
16+
17+
export function getHelpString(): string {
18+
let result = helpString + "\n\n";
19+
20+
result += "Options:\n";
21+
for (const option of optionDeclarations) {
22+
const aliasStrings = (option.aliases || []).map(a => "-" + a);
23+
const optionString = aliasStrings.concat(["--" + option.name]).join("|");
24+
25+
const valuesHint = option.type === "enum" ? option.choices.join("|") : option.type;
26+
const spacing = " ".repeat(Math.max(1, 45 - optionString.length - valuesHint.length));
27+
28+
result += `\n ${optionString} <${valuesHint}>${spacing}${option.description}\n`;
29+
}
30+
31+
return result;
32+
}
Lines changed: 9 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import * as path from "path";
21
import * as ts from "typescript";
3-
import { CompilerOptions, LuaLibImportKind, LuaTarget } from "./CompilerOptions";
4-
import * as diagnosticFactories from "./diagnostics";
2+
import { CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions";
3+
import * as cliDiagnostics from "./diagnostics";
54

65
export interface ParsedCommandLine extends ts.ParsedCommandLine {
76
options: CompilerOptions;
@@ -24,7 +23,7 @@ interface CommandLineOptionOfBoolean extends CommandLineOptionBase {
2423

2524
type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfBoolean;
2625

27-
const optionDeclarations: CommandLineOption[] = [
26+
export const optionDeclarations: CommandLineOption[] = [
2827
{
2928
name: "luaLibImport",
3029
description: "Specifies how js standard features missing in lua are imported.",
@@ -60,36 +59,6 @@ const optionDeclarations: CommandLineOption[] = [
6059
},
6160
];
6261

63-
export const version = `Version ${require("../package.json").version}`;
64-
65-
const helpString = `
66-
Syntax: tstl [options] [files...]
67-
68-
Examples: tstl path/to/file.ts [...]
69-
tstl -p path/to/tsconfig.json
70-
71-
In addition to the options listed below you can also pass options
72-
for the typescript compiler (For a list of options use tsc -h).
73-
Some tsc options might have no effect.
74-
`.trim();
75-
76-
export function getHelpString(): string {
77-
let result = helpString + "\n\n";
78-
79-
result += "Options:\n";
80-
for (const option of optionDeclarations) {
81-
const aliasStrings = (option.aliases || []).map(a => "-" + a);
82-
const optionString = aliasStrings.concat(["--" + option.name]).join("|");
83-
84-
const valuesHint = option.type === "enum" ? option.choices.join("|") : option.type;
85-
const spacing = " ".repeat(Math.max(1, 45 - optionString.length - valuesHint.length));
86-
87-
result += `\n ${optionString} <${valuesHint}>${spacing}${option.description}\n`;
88-
}
89-
90-
return result;
91-
}
92-
9362
export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine {
9463
let hasRootLevelOptions = false;
9564
for (const key in parsedConfigFile.raw) {
@@ -103,15 +72,13 @@ export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine):
10372

10473
if (parsedConfigFile.raw.tstl) {
10574
if (hasRootLevelOptions) {
106-
parsedConfigFile.errors.push(
107-
diagnosticFactories.tstlOptionsAreMovingToTheTstlObject(parsedConfigFile.raw.tstl)
108-
);
75+
parsedConfigFile.errors.push(cliDiagnostics.tstlOptionsAreMovingToTheTstlObject(parsedConfigFile.raw.tstl));
10976
}
11077

11178
for (const key in parsedConfigFile.raw.tstl) {
11279
const option = optionDeclarations.find(option => option.name === key);
11380
if (!option) {
114-
parsedConfigFile.errors.push(diagnosticFactories.unknownCompilerOption(key));
81+
parsedConfigFile.errors.push(cliDiagnostics.unknownCompilerOption(key));
11582
continue;
11683
}
11784

@@ -179,7 +146,7 @@ function readCommandLineArgument(option: CommandLineOption, value: any): Command
179146

180147
if (value === undefined) {
181148
return {
182-
error: diagnosticFactories.compilerOptionExpectsAnArgument(option.name),
149+
error: cliDiagnostics.compilerOptionExpectsAnArgument(option.name),
183150
value: undefined,
184151
increment: 0,
185152
};
@@ -201,7 +168,7 @@ function readValue(option: CommandLineOption, value: unknown): ReadValueResult {
201168
if (typeof value !== "boolean") {
202169
return {
203170
value: undefined,
204-
error: diagnosticFactories.compilerOptionRequiresAValueOfType(option.name, "boolean"),
171+
error: cliDiagnostics.compilerOptionRequiresAValueOfType(option.name, "boolean"),
205172
};
206173
}
207174

@@ -212,7 +179,7 @@ function readValue(option: CommandLineOption, value: unknown): ReadValueResult {
212179
if (typeof value !== "string") {
213180
return {
214181
value: undefined,
215-
error: diagnosticFactories.compilerOptionRequiresAValueOfType(option.name, "string"),
182+
error: cliDiagnostics.compilerOptionRequiresAValueOfType(option.name, "string"),
216183
};
217184
}
218185

@@ -221,38 +188,11 @@ function readValue(option: CommandLineOption, value: unknown): ReadValueResult {
221188
const optionChoices = option.choices.join(", ");
222189
return {
223190
value: undefined,
224-
error: diagnosticFactories.argumentForOptionMustBe(`--${option.name}`, optionChoices),
191+
error: cliDiagnostics.argumentForOptionMustBe(`--${option.name}`, optionChoices),
225192
};
226193
}
227194

228195
return { value: enumValue };
229196
}
230197
}
231198
}
232-
233-
export function parseConfigFileWithSystem(
234-
configFileName: string,
235-
commandLineOptions?: CompilerOptions,
236-
system = ts.sys
237-
): ParsedCommandLine {
238-
const parsedConfigFile = ts.parseJsonSourceFileConfigFileContent(
239-
ts.readJsonConfigFile(configFileName, system.readFile),
240-
system,
241-
path.dirname(configFileName),
242-
commandLineOptions,
243-
configFileName
244-
);
245-
246-
return updateParsedConfigFile(parsedConfigFile);
247-
}
248-
249-
export function createDiagnosticReporter(pretty: boolean, system = ts.sys): ts.DiagnosticReporter {
250-
const reporter: ts.DiagnosticReporter = (ts as any).createDiagnosticReporter(system, pretty);
251-
return diagnostic => {
252-
if (diagnostic.source === "typescript-to-lua") {
253-
diagnostic = { ...diagnostic, code: ("TL" + diagnostic.code) as any };
254-
}
255-
256-
reporter(diagnostic);
257-
};
258-
}

0 commit comments

Comments
 (0)