196 lines
8.3 KiB
JavaScript
196 lines
8.3 KiB
JavaScript
"use strict";
|
|
/**
|
|
* @license
|
|
* Copyright 2013 Palantir Technologies, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
// tslint:disable strict-boolean-expressions prefer-template
|
|
// (wait on https://github.com/palantir/tslint/pull/2572)
|
|
var fs = require("fs");
|
|
var glob = require("glob");
|
|
var path = require("path");
|
|
var ts = require("typescript");
|
|
var configuration_1 = require("./configuration");
|
|
var error_1 = require("./error");
|
|
var Linter = require("./linter");
|
|
var test_1 = require("./test");
|
|
var Runner = (function () {
|
|
function Runner(options, outputStream) {
|
|
this.options = options;
|
|
this.outputStream = outputStream;
|
|
}
|
|
Runner.trimSingleQuotes = function (str) {
|
|
return str.replace(/^'|'$/g, "");
|
|
};
|
|
Runner.prototype.run = function (onComplete) {
|
|
var _this = this;
|
|
if (this.options.version) {
|
|
this.outputStream.write(Linter.VERSION + "\n");
|
|
return onComplete(0);
|
|
}
|
|
if (this.options.init) {
|
|
if (fs.existsSync(configuration_1.CONFIG_FILENAME)) {
|
|
console.error("Cannot generate " + configuration_1.CONFIG_FILENAME + ": file already exists");
|
|
return onComplete(1);
|
|
}
|
|
var tslintJSON = JSON.stringify(configuration_1.DEFAULT_CONFIG, undefined, " ");
|
|
fs.writeFileSync(configuration_1.CONFIG_FILENAME, tslintJSON);
|
|
return onComplete(0);
|
|
}
|
|
if (this.options.test) {
|
|
var results = test_1.runTests((this.options.files || []).map(Runner.trimSingleQuotes), this.options.rulesDirectory);
|
|
var didAllTestsPass = test_1.consoleTestResultsHandler(results);
|
|
return onComplete(didAllTestsPass ? 0 : 1);
|
|
}
|
|
// when provided, it should point to an existing location
|
|
if (this.options.config && !fs.existsSync(this.options.config)) {
|
|
console.error("Invalid option for configuration: " + this.options.config);
|
|
return onComplete(1);
|
|
}
|
|
// if both files and tsconfig are present, use files
|
|
var files = this.options.files === undefined ? [] : this.options.files;
|
|
var program;
|
|
if (this.options.project != null) {
|
|
var project = findTsconfig(this.options.project);
|
|
if (project === undefined) {
|
|
console.error("Invalid option for project: " + this.options.project);
|
|
return onComplete(1);
|
|
}
|
|
program = Linter.createProgram(project);
|
|
if (files.length === 0) {
|
|
files = Linter.getFileNames(program);
|
|
}
|
|
if (this.options.typeCheck) {
|
|
// if type checking, run the type checker
|
|
var diagnostics = ts.getPreEmitDiagnostics(program);
|
|
if (diagnostics.length > 0) {
|
|
var messages = diagnostics.map(function (diag) {
|
|
// emit any error messages
|
|
var message = ts.DiagnosticCategory[diag.category];
|
|
if (diag.file) {
|
|
var _a = diag.file.getLineAndCharacterOfPosition(diag.start), line = _a.line, character = _a.character;
|
|
var file = void 0;
|
|
var currentDirectory = program.getCurrentDirectory();
|
|
file = _this.options.outputAbsolutePaths
|
|
? path.resolve(currentDirectory, diag.file.fileName)
|
|
: path.relative(currentDirectory, diag.file.fileName);
|
|
message += " at " + file + ":" + (line + 1) + ":" + (character + 1) + ":";
|
|
}
|
|
message += " " + ts.flattenDiagnosticMessageText(diag.messageText, "\n");
|
|
return message;
|
|
});
|
|
console.error(messages.join("\n"));
|
|
return onComplete(this.options.force ? 0 : 1);
|
|
}
|
|
}
|
|
else {
|
|
// if not type checking, we don't need to pass in a program object
|
|
program = undefined;
|
|
}
|
|
}
|
|
var ignorePatterns = [];
|
|
if (this.options.exclude) {
|
|
var excludeArguments = Array.isArray(this.options.exclude) ? this.options.exclude : [this.options.exclude];
|
|
ignorePatterns = excludeArguments.map(Runner.trimSingleQuotes);
|
|
}
|
|
files = files
|
|
.map(Runner.trimSingleQuotes)
|
|
.map(function (file) { return glob.sync(file, { ignore: ignorePatterns, nodir: true }); })
|
|
.reduce(function (a, b) { return a.concat(b); }, [])
|
|
.map(function (file) {
|
|
if (_this.options.outputAbsolutePaths) {
|
|
return path.resolve(file);
|
|
}
|
|
return path.relative(process.cwd(), file);
|
|
});
|
|
try {
|
|
this.processFiles(onComplete, files, program);
|
|
}
|
|
catch (error) {
|
|
if (error.name === error_1.FatalError.NAME) {
|
|
console.error(error.message);
|
|
return onComplete(1);
|
|
}
|
|
// rethrow unhandled error
|
|
throw error;
|
|
}
|
|
};
|
|
Runner.prototype.processFiles = function (onComplete, files, program) {
|
|
var _this = this;
|
|
var possibleConfigAbsolutePath = this.options.config != null ? path.resolve(this.options.config) : null;
|
|
var linter = new Linter({
|
|
fix: !!this.options.fix,
|
|
formatter: this.options.format,
|
|
formattersDirectory: this.options.formattersDirectory || "",
|
|
rulesDirectory: this.options.rulesDirectory || "",
|
|
}, program);
|
|
var lastFolder;
|
|
var configFile;
|
|
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
|
|
var file = files_1[_i];
|
|
if (!fs.existsSync(file)) {
|
|
console.error("Unable to open file: " + file);
|
|
return onComplete(1);
|
|
}
|
|
var buffer = new Buffer(256);
|
|
var fd = fs.openSync(file, "r");
|
|
try {
|
|
fs.readSync(fd, buffer, 0, 256, 0);
|
|
if (buffer.readInt8(0, true) === 0x47 && buffer.readInt8(188, true) === 0x47) {
|
|
// MPEG transport streams use the '.ts' file extension. They use 0x47 as the frame
|
|
// separator, repeating every 188 bytes. It is unlikely to find that pattern in
|
|
// TypeScript source, so tslint ignores files with the specific pattern.
|
|
console.warn(file + ": ignoring MPEG transport stream");
|
|
continue;
|
|
}
|
|
}
|
|
finally {
|
|
fs.closeSync(fd);
|
|
}
|
|
var contents = fs.readFileSync(file, "utf8");
|
|
var folder = path.dirname(file);
|
|
if (lastFolder !== folder) {
|
|
configFile = configuration_1.findConfiguration(possibleConfigAbsolutePath, folder).results;
|
|
lastFolder = folder;
|
|
}
|
|
linter.lint(file, contents, configFile);
|
|
}
|
|
var lintResult = linter.getResult();
|
|
this.outputStream.write(lintResult.output, function () {
|
|
if (_this.options.force || lintResult.errorCount === 0) {
|
|
onComplete(0);
|
|
}
|
|
else {
|
|
onComplete(2);
|
|
}
|
|
});
|
|
};
|
|
return Runner;
|
|
}());
|
|
exports.Runner = Runner;
|
|
function findTsconfig(project) {
|
|
try {
|
|
var 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;
|
|
}
|