diff --git a/src/runner.ts b/src/runner.ts index 427f361de18..ac5e9ee38be 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -146,11 +146,12 @@ export class Runner { let program: ts.Program | undefined; if (this.options.project != null) { - if (!fs.existsSync(this.options.project)) { + const project = findTsconfig(this.options.project); + if (project === undefined) { console.error("Invalid option for project: " + this.options.project); return onComplete(1); } - program = Linter.createProgram(this.options.project); + program = Linter.createProgram(project); if (files.length === 0) { files = Linter.getFileNames(program); } @@ -175,9 +176,6 @@ export class Runner { // if not type checking, we don't need to pass in a program object program = undefined; } - } else if (this.options.typeCheck) { - console.error("--project must be specified in order to enable type checking."); - return onComplete(1); } let ignorePatterns: string[] = []; @@ -257,3 +255,16 @@ export class Runner { }); } } + +function findTsconfig(project: string): string | undefined { + try { + const stats = fs.statSync(project); // throws if file does not exist + if (stats.isDirectory()) { + project = path.join(project, "tsconfig.json"); + fs.accessSync(project); // throws if file does not exist + } + } catch (e) { + return undefined; + } + return project; +} diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts index b53d4995e17..79585133537 100644 --- a/src/tslint-cli.ts +++ b/src/tslint-cli.ts @@ -54,6 +54,12 @@ const processed = optimist throw "Missing files"; } + // tslint:disable-next-line strict-boolean-expressions + if (argv["type-check"] && !argv.project) { + // tslint:disable-next-line:no-string-throw + throw "--project must be specified in order to enable type checking."; + } + // tslint:disable-next-line strict-boolean-expressions if (argv.f) { // throw a string, otherwise a call stack is printed for this message @@ -214,7 +220,7 @@ tslint accepts the following commandline options: this can be used to test custom rules. -p, --project: - The location of a tsconfig.json file that will be used to determine which + The path or directory containing a tsconfig.json file that will be used to determine which files will be linted. --type-check diff --git a/test/executable/executableTests.ts b/test/executable/executableTests.ts index b11046416e4..9e7db3b1ca2 100644 --- a/test/executable/executableTests.ts +++ b/test/executable/executableTests.ts @@ -251,6 +251,29 @@ describe("Executable", function(this: Mocha.ISuiteCallbackContext) { }); }); + it("can be passed a directory and defaults to tsconfig.json", (done) => { + execCli(["-c", "test/files/tsconfig-test/tslint.json", "--project", "test/files/tsconfig-test"], (err) => { + assert.isNull(err, "process should exit without an error"); + done(); + }); + }); + + it("exits with error if passed a directory and there is not tsconfig.json", (done) => { + execCli(["-c", "test/files/tsconfig-test/tslint.json", "--project", "test/files"], (err) => { + assert.isNotNull(err, "process should exit with an error"); + assert.strictEqual(err.code, 1, "error code should be 1"); + done(); + }); + }); + + it("exits with error if passed directory does not exist", (done) => { + execCli(["-c", "test/files/tsconfig-test/tslint.json", "--project", "test/files/non-existant"], (err) => { + assert.isNotNull(err, "process should exit with an error"); + assert.strictEqual(err.code, 1, "error code should be 1"); + done(); + }); + }); + it("exits with code 2 if both `tsconfig.json` and files arguments are passed and files contain lint errors", (done) => { execCli( [