UNPKG

13.4 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright 2013 Palantir Technologies, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18Object.defineProperty(exports, "__esModule", { value: true });
19var tslib_1 = require("tslib");
20// tslint:disable strict-boolean-expressions (TODO: Fix up options)
21var fs = require("fs");
22var glob = require("glob");
23var minimatch_1 = require("minimatch");
24var path = require("path");
25var ts = require("typescript");
26var configuration_1 = require("./configuration");
27var error_1 = require("./error");
28var linter_1 = require("./linter");
29var utils_1 = require("./utils");
30function run(options, logger) {
31 return tslib_1.__awaiter(this, void 0, void 0, function () {
32 var error_2;
33 return tslib_1.__generator(this, function (_a) {
34 switch (_a.label) {
35 case 0:
36 _a.trys.push([0, 2, , 3]);
37 return [4 /*yield*/, runWorker(options, logger)];
38 case 1: return [2 /*return*/, _a.sent()];
39 case 2:
40 error_2 = _a.sent();
41 if (error_2 instanceof error_1.FatalError) {
42 logger.error(error_2.message + "\n");
43 return [2 /*return*/, 1 /* FatalError */];
44 }
45 throw error_2;
46 case 3: return [2 /*return*/];
47 }
48 });
49 });
50}
51exports.run = run;
52function runWorker(options, logger) {
53 return tslib_1.__awaiter(this, void 0, void 0, function () {
54 var test_1, results, _a, output, errorCount;
55 return tslib_1.__generator(this, function (_b) {
56 switch (_b.label) {
57 case 0:
58 if (options.init) {
59 if (fs.existsSync(configuration_1.JSON_CONFIG_FILENAME)) {
60 throw new error_1.FatalError("Cannot generate " + configuration_1.JSON_CONFIG_FILENAME + ": file already exists");
61 }
62 fs.writeFileSync(configuration_1.JSON_CONFIG_FILENAME, JSON.stringify(configuration_1.DEFAULT_CONFIG, undefined, " "));
63 return [2 /*return*/, 0 /* Ok */];
64 }
65 if (!options.test) return [3 /*break*/, 2];
66 return [4 /*yield*/, Promise.resolve().then(function () { return require("./test"); })];
67 case 1:
68 test_1 = _b.sent();
69 results = test_1.runTests((options.files || []).map(trimSingleQuotes), options.rulesDirectory);
70 return [2 /*return*/, test_1.consoleTestResultsHandler(results, logger) ? 0 /* Ok */ : 1 /* FatalError */];
71 case 2:
72 if (options.config && !fs.existsSync(options.config)) {
73 throw new error_1.FatalError("Invalid option for configuration: " + options.config);
74 }
75 return [4 /*yield*/, runLinter(options, logger)];
76 case 3:
77 _a = _b.sent(), output = _a.output, errorCount = _a.errorCount;
78 if (output && output.trim()) {
79 logger.log(output + "\n");
80 }
81 return [2 /*return*/, options.force || errorCount === 0 ? 0 /* Ok */ : 2 /* LintError */];
82 }
83 });
84 });
85}
86function runLinter(options, logger) {
87 return tslib_1.__awaiter(this, void 0, void 0, function () {
88 var _a, files, program, diagnostics, message;
89 return tslib_1.__generator(this, function (_b) {
90 _a = resolveFilesAndProgram(options, logger), files = _a.files, program = _a.program;
91 // if type checking, run the type checker
92 if (program && options.typeCheck) {
93 diagnostics = ts.getPreEmitDiagnostics(program);
94 if (diagnostics.length !== 0) {
95 message = diagnostics
96 .map(function (d) { return showDiagnostic(d, program, options.outputAbsolutePaths); })
97 .join("\n");
98 if (options.force) {
99 logger.error(message + "\n");
100 }
101 else {
102 throw new error_1.FatalError(message);
103 }
104 }
105 }
106 return [2 /*return*/, doLinting(options, files, program, logger)];
107 });
108 });
109}
110function resolveFilesAndProgram(_a, logger) {
111 var files = _a.files, project = _a.project, exclude = _a.exclude, outputAbsolutePaths = _a.outputAbsolutePaths;
112 // remove single quotes which break matching on Windows when glob is passed in single quotes
113 exclude = exclude.map(trimSingleQuotes);
114 if (project === undefined) {
115 return { files: resolveGlobs(files, exclude, outputAbsolutePaths, logger) };
116 }
117 var projectPath = findTsconfig(project);
118 if (projectPath === undefined) {
119 throw new error_1.FatalError("Invalid option for project: " + project);
120 }
121 exclude = exclude.map(function (pattern) { return path.resolve(pattern); });
122 var program = linter_1.Linter.createProgram(projectPath);
123 var filesFound;
124 if (files.length === 0) {
125 filesFound = filterFiles(linter_1.Linter.getFileNames(program), exclude, false);
126 }
127 else {
128 files = files.map(function (f) { return path.resolve(f); });
129 filesFound = filterFiles(program.getSourceFiles().map(function (f) { return f.fileName; }), files, true);
130 filesFound = filterFiles(filesFound, exclude, false);
131 // find non-glob files that have no matching file in the project and are not excluded by any exclude pattern
132 for (var _i = 0, _b = filterFiles(files, exclude, false); _i < _b.length; _i++) {
133 var file = _b[_i];
134 if (!glob.hasMagic(file) && !filesFound.some(minimatch_1.filter(file))) {
135 if (fs.existsSync(file)) {
136 throw new error_1.FatalError("'" + file + "' is not included in project.");
137 }
138 logger.error("'" + file + "' does not exist. This will be an error in TSLint 6.\n"); // TODO make this an error in v6.0.0
139 }
140 }
141 }
142 return { files: filesFound, program: program };
143}
144function filterFiles(files, patterns, include) {
145 if (patterns.length === 0) {
146 return include ? [] : files;
147 }
148 var matcher = patterns.map(function (pattern) { return new minimatch_1.Minimatch(pattern, { dot: !include }); }); // `glob` always enables `dot` for ignore patterns
149 return files.filter(function (file) { return include === matcher.some(function (pattern) { return pattern.match(file); }); });
150}
151function resolveGlobs(files, ignore, outputAbsolutePaths, logger) {
152 var results = utils_1.flatMap(files, function (file) {
153 return glob.sync(trimSingleQuotes(file), { ignore: ignore, nodir: true });
154 });
155 // warn if `files` contains non-existent files, that are not patters and not excluded by any of the exclude patterns
156 for (var _i = 0, _a = filterFiles(files, ignore, false); _i < _a.length; _i++) {
157 var file = _a[_i];
158 if (!glob.hasMagic(file) && !results.some(minimatch_1.filter(file))) {
159 logger.error("'" + file + "' does not exist. This will be an error in TSLint 6.\n"); // TODO make this an error in v6.0.0
160 }
161 }
162 var cwd = process.cwd();
163 return results.map(function (file) { return (outputAbsolutePaths ? path.resolve(cwd, file) : path.relative(cwd, file)); });
164}
165function doLinting(options, files, program, logger) {
166 return tslib_1.__awaiter(this, void 0, void 0, function () {
167 var configFile, formatter, linter, lastFolder, _i, files_1, file, folder, contents, sourceFile;
168 return tslib_1.__generator(this, function (_a) {
169 switch (_a.label) {
170 case 0:
171 configFile = options.config !== undefined ? configuration_1.findConfiguration(options.config).results : undefined;
172 formatter = options.format;
173 if (formatter === undefined) {
174 formatter =
175 configFile && configFile.linterOptions && configFile.linterOptions.format
176 ? configFile.linterOptions.format
177 : "prose";
178 }
179 linter = new linter_1.Linter({
180 fix: !!options.fix,
181 formatter: formatter,
182 formattersDirectory: options.formattersDirectory,
183 quiet: !!options.quiet,
184 rulesDirectory: options.rulesDirectory,
185 }, program);
186 _i = 0, files_1 = files;
187 _a.label = 1;
188 case 1:
189 if (!(_i < files_1.length)) return [3 /*break*/, 6];
190 file = files_1[_i];
191 if (options.config === undefined) {
192 folder = path.dirname(file);
193 if (lastFolder !== folder) {
194 configFile = configuration_1.findConfiguration(null, folder).results;
195 lastFolder = folder;
196 }
197 }
198 if (configuration_1.isFileExcluded(file, configFile)) {
199 return [3 /*break*/, 5];
200 }
201 contents = void 0;
202 if (!(program !== undefined)) return [3 /*break*/, 2];
203 sourceFile = program.getSourceFile(file);
204 if (sourceFile !== undefined) {
205 contents = sourceFile.text;
206 }
207 return [3 /*break*/, 4];
208 case 2: return [4 /*yield*/, tryReadFile(file, logger)];
209 case 3:
210 contents = _a.sent();
211 _a.label = 4;
212 case 4:
213 if (contents !== undefined) {
214 linter.lint(file, contents, configFile);
215 }
216 _a.label = 5;
217 case 5:
218 _i++;
219 return [3 /*break*/, 1];
220 case 6: return [2 /*return*/, linter.getResult()];
221 }
222 });
223 });
224}
225/** Read a file, but return undefined if it is an MPEG '.ts' file. */
226function tryReadFile(filename, logger) {
227 return tslib_1.__awaiter(this, void 0, void 0, function () {
228 var buffer, fd;
229 return tslib_1.__generator(this, function (_a) {
230 if (!fs.existsSync(filename)) {
231 throw new error_1.FatalError("Unable to open file: " + filename);
232 }
233 buffer = Buffer.allocUnsafe(256);
234 fd = fs.openSync(filename, "r");
235 try {
236 fs.readSync(fd, buffer, 0, 256, 0);
237 if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) {
238 // MPEG transport streams use the '.ts' file extension. They use 0x47 as the frame
239 // separator, repeating every 188 bytes. It is unlikely to find that pattern in
240 // TypeScript source, so tslint ignores files with the specific pattern.
241 logger.error(filename + ": ignoring MPEG transport stream\n");
242 return [2 /*return*/, undefined];
243 }
244 }
245 finally {
246 fs.closeSync(fd);
247 }
248 return [2 /*return*/, fs.readFileSync(filename, "utf8")];
249 });
250 });
251}
252function showDiagnostic(_a, program, outputAbsolutePaths) {
253 var file = _a.file, start = _a.start, category = _a.category, messageText = _a.messageText;
254 var message = ts.DiagnosticCategory[category];
255 if (file !== undefined && start !== undefined) {
256 var _b = file.getLineAndCharacterOfPosition(start), line = _b.line, character = _b.character;
257 var currentDirectory = program.getCurrentDirectory();
258 var filePath = outputAbsolutePaths
259 ? path.resolve(currentDirectory, file.fileName)
260 : path.relative(currentDirectory, file.fileName);
261 message += " at " + filePath + ":" + (line + 1) + ":" + (character + 1) + ":";
262 }
263 return message + " " + ts.flattenDiagnosticMessageText(messageText, "\n");
264}
265function trimSingleQuotes(str) {
266 return str.replace(/^'|'$/g, "");
267}
268function findTsconfig(project) {
269 try {
270 var stats = fs.statSync(project); // throws if file does not exist
271 if (!stats.isDirectory()) {
272 return project;
273 }
274 var projectFile = path.join(project, "tsconfig.json");
275 fs.accessSync(projectFile); // throws if file does not exist
276 return projectFile;
277 }
278 catch (e) {
279 return undefined;
280 }
281}